Anonymous avatar Anonymous committed d10b61e

NativeCrypto: Add ALPN support

This adds the ability to use Application-Layer Protocol Negotiation
(ALPN) as both a client and a server. ALPN is essentially like Next
Protocol Negotiation (NPN) but negotiation is done in the clear. This
allows the use of other protocols on the same port (e.g., SPDY instead
of HTTP on port 80).

Although previously clients using NPN were able to use cut-through, the
new ALPN API does not provide for a way for a client to enable that
during a callback. So the only difference is that NPN clients can enable
SSL False Start while ALPN clients cannot currently.

Change-Id: I42ff70f3711e9cccaf754d189f76eeaa9db5f981

Comments (0)

Files changed (4)

crypto/src/main/java/org/conscrypt/NativeCrypto.java

     public static native void SSL_CTX_disable_npn(long sslCtxNativePointer);
 
     /**
-     * Returns the sslSessionNativePointer of the negotiated session
+     * For clients, sets the list of supported ALPN protocols in wire-format
+     * (length-prefixed 8-bit strings) on an SSL context.
+     */
+    public static native int SSL_CTX_set_alpn_protos(long sslCtxPointer, byte[] protos);
+
+    /**
+     * Returns the selected ALPN protocol. If the server did not select a
+     * protocol, {@code null} will be returned.
+     */
+    public static native byte[] SSL_get0_alpn_selected(long sslPointer);
+
+    /**
+     * Returns the sslSessionNativePointer of the negotiated session. If this is
+     * a server negotiation, supplying the {@code alpnProtocols} will enable
+     * ALPN negotiation.
      */
     public static native int SSL_do_handshake(long sslNativePointer,
                                               FileDescriptor fd,
                                               SSLHandshakeCallbacks shc,
                                               int timeoutMillis,
                                               boolean client_mode,
-                                              byte[] npnProtocols)
+                                              byte[] npnProtocols,
+                                              byte[] alpnProtocols)
         throws SSLException, SocketTimeoutException, CertificateException;
 
     public static native byte[] SSL_get_npn_negotiated_protocol(long sslNativePointer);

crypto/src/main/java/org/conscrypt/OpenSSLSocketImpl.java

     private final Object writeLock = new Object();
     private SSLParametersImpl sslParameters;
     private byte[] npnProtocols;
+    private byte[] alpnProtocols;
     private String[] enabledProtocols;
     private String[] enabledCipherSuites;
     private boolean useSessionTickets;
                 NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer);
             }
 
+            if (client && alpnProtocols != null) {
+                NativeCrypto.SSL_CTX_set_alpn_protos(sslCtxNativePointer, alpnProtocols);
+            }
+
             // setup server certificates and private keys.
             // clients will receive a call back to request certificates.
             if (!client) {
             int sslSessionNativePointer;
             try {
                 sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer,
-                        socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols);
+                        socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols,
+                        client ? null : alpnProtocols);
             } catch (CertificateException e) {
                 SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
                 wrapper.initCause(e);
     }
 
     /**
+     * Returns the protocol agreed upon by client and server, or {@code null} if
+     * no protocol was agreed upon.
+     */
+    public byte[] getAlpnSelectedProtocol() {
+        return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer);
+    }
+
+    /**
      * Sets the list of protocols this peer is interested in. If null no
      * protocols will be used.
      *
         }
         this.npnProtocols = npnProtocols;
     }
+
+    /**
+     * Sets the list of protocols this peer is interested in. If the list is
+     * {@code null}, no protocols will be used.
+     *
+     * @param alpnProtocols a non-empty array of protocol names. From
+     *            SSL_select_next_proto, "vector of 8-bit, length prefixed byte
+     *            strings. The length byte itself is not included in the length.
+     *            A byte string of length 0 is invalid. No byte string may be
+     *            truncated.".
+     */
+    public void setAlpnProtocols(byte[] alpnProtocols) {
+        if (alpnProtocols != null && alpnProtocols.length == 0) {
+            throw new IllegalArgumentException("alpnProtocols.length == 0");
+        }
+        this.alpnProtocols = alpnProtocols;
+    }
 }

crypto/src/main/native/org_conscrypt_NativeCrypto.cpp

     jbyteArray npnProtocolsArray;
     jbyte* npnProtocolsData;
     size_t npnProtocolsLength;
+    jbyteArray alpnProtocolsArray;
+    jbyte* alpnProtocolsData;
+    size_t alpnProtocolsLength;
     Unique_RSA ephemeralRsa;
     Unique_EC_KEY ephemeralEc;
 
             npnProtocolsArray(NULL),
             npnProtocolsData(NULL),
             npnProtocolsLength(-1),
+            alpnProtocolsArray(NULL),
+            alpnProtocolsData(NULL),
+            alpnProtocolsLength(-1),
             ephemeralRsa(NULL),
             ephemeralEc(NULL) {
         fdsEmergency[0] = -1;
      * @param npnProtocols NPN protocols so that they may be advertised (by the
      *                     server) or selected (by the client). Has no effect
      *                     unless NPN is enabled.
+     * @param alpnProtocols ALPN protocols so that they may be advertised (by the
+     *                     server) or selected (by the client). Passing non-NULL
+     *                     enables ALPN.
      */
-    bool setCallbackState(JNIEnv* e, jobject shc, jobject fd, jbyteArray npnProtocols) {
+    bool setCallbackState(JNIEnv* e, jobject shc, jobject fd, jbyteArray npnProtocols,
+            jbyteArray alpnProtocols) {
         NetFd netFd(e, fd);
         if (netFd.isClosed()) {
             return false;
                 return false;
             }
         }
+        if (alpnProtocols != NULL) {
+            alpnProtocolsArray = alpnProtocols;
+            alpnProtocolsLength = e->GetArrayLength(alpnProtocols);
+            alpnProtocolsData = e->GetByteArrayElements(alpnProtocols, NULL);
+            if (alpnProtocolsData == NULL) {
+                return false;
+            }
+        }
         return true;
     }
 
             npnProtocolsData = NULL;
             npnProtocolsLength = -1;
         }
+        if (alpnProtocolsArray != NULL) {
+            env->ReleaseByteArrayElements(alpnProtocolsArray, alpnProtocolsData, JNI_ABORT);
+            alpnProtocolsArray = NULL;
+            alpnProtocolsData = NULL;
+            alpnProtocolsLength = -1;
+        }
         env = NULL;
     }
 
 }
 
 /**
- * Callback for the client to select a protocol.
+ * A common selection path for both NPN and ALPN since they're essentially the
+ * same protocol. The list of protocols in "primary" is considered the order
+ * which should take precedence.
  */
-static int next_proto_select_callback(SSL* ssl, unsigned char **out, unsigned char *outlen,
-        const unsigned char *in, unsigned int inlen, void *)
-{
-    JNI_TRACE("ssl=%p next_proto_select_callback", ssl);
-
-    // Enable False Start on the client if the server understands NPN
-    // http://www.imperialviolet.org/2012/04/11/falsestart.html
-    SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
-
-    AppData* appData = toAppData(ssl);
-    JNI_TRACE("AppData=%p", appData);
-    unsigned char* npnProtocols = reinterpret_cast<unsigned char*>(appData->npnProtocolsData);
-    if (npnProtocols != NULL) {
-        size_t npnProtocolsLength = appData->npnProtocolsLength;
-        JNI_TRACE("npn_protocols=%p, length=%d", npnProtocols, npnProtocolsLength);
-
-        int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtocols,
-                npnProtocolsLength);
+static int proto_select(SSL* ssl, unsigned char **out, unsigned char *outLength,
+        const unsigned char *primary, const unsigned int primaryLength,
+        const unsigned char *secondary, const unsigned int secondaryLength) {
+    if (primary != NULL) {
+        JNI_TRACE("primary=%p, length=%d", primary, primaryLength);
+
+        int status = SSL_select_next_proto(out, outLength, primary, primaryLength, secondary,
+                secondaryLength);
         switch (status) {
         case OPENSSL_NPN_NEGOTIATED:
-            JNI_TRACE("ssl=%p next_proto_select_callback NPN negotiated", ssl);
+            JNI_TRACE("ssl=%p proto_select NPN/ALPN negotiated", ssl);
             break;
         case OPENSSL_NPN_UNSUPPORTED:
-            JNI_TRACE("ssl=%p next_proto_select_callback NPN unsupported", ssl);
+            JNI_TRACE("ssl=%p proto_select NPN/ALPN unsupported", ssl);
             break;
         case OPENSSL_NPN_NO_OVERLAP:
-            JNI_TRACE("ssl=%p next_proto_select_callback NPN no overlap", ssl);
+            JNI_TRACE("ssl=%p proto_select NPN/ALPN no overlap", ssl);
             break;
         }
     } else {
-        JNI_TRACE("npn_protocols=NULL");
+        JNI_TRACE("protocols=NULL");
     }
     return SSL_TLSEXT_ERR_OK;
 }
 
 /**
+ * Callback for the server to select an ALPN protocol.
+ */
+static int alpn_select_callback(SSL* ssl, const unsigned char **out, unsigned char *outlen,
+        const unsigned char *in, unsigned int inlen, void *) {
+    JNI_TRACE("ssl=%p alpn_select_callback", ssl);
+
+    AppData* appData = toAppData(ssl);
+    JNI_TRACE("AppData=%p", appData);
+
+    return proto_select(ssl, const_cast<unsigned char **>(out), outlen,
+            reinterpret_cast<unsigned char*>(appData->alpnProtocolsData),
+            appData->alpnProtocolsLength, in, inlen);
+}
+
+/**
+ * Callback for the client to select an NPN protocol.
+ */
+static int next_proto_select_callback(SSL* ssl, unsigned char **out, unsigned char *outlen,
+        const unsigned char *in, unsigned int inlen, void *arg)
+{
+    JNI_TRACE("ssl=%p next_proto_select_callback", ssl);
+
+    AppData* appData = toAppData(ssl);
+    JNI_TRACE("AppData=%p", appData);
+
+    // Enable False Start on the client if the server understands NPN
+    // http://www.imperialviolet.org/2012/04/11/falsestart.html
+    SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
+
+    return proto_select(ssl, out, outlen, in, inlen,
+            reinterpret_cast<unsigned char*>(appData->npnProtocolsData),
+            appData->npnProtocolsLength);
+}
+
+/**
  * Callback for the server to advertise available protocols.
  */
 static int next_protos_advertised_callback(SSL* ssl,
     return result;
 }
 
+static int NativeCrypto_SSL_CTX_set_alpn_protos(JNIEnv* env, jclass, jlong ssl_ctx_address,
+        jbyteArray protos) {
+    SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+    if (ssl_ctx == NULL) {
+        return 0;
+    }
+
+    JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=%p", ssl_ctx, protos);
+
+    if (protos == NULL) {
+        JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=NULL", ssl_ctx);
+        return 1;
+    }
+
+    ScopedByteArrayRO protosBytes(env, protos);
+    if (protosBytes.get() == NULL) {
+        JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=%p => protosBytes == NULL", ssl_ctx,
+                protos);
+        return 0;
+    }
+
+    const unsigned char *tmp = reinterpret_cast<const unsigned char*>(protosBytes.get());
+    int ret = SSL_CTX_set_alpn_protos(ssl_ctx, tmp, protosBytes.size());
+    JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=%p => ret=%d", ssl_ctx, protos, ret);
+    return ret;
+}
+
+static jbyteArray NativeCrypto_SSL_get0_alpn_selected(JNIEnv* env, jclass,
+        jlong ssl_address)
+{
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p SSL_get0_alpn_selected", ssl);
+    if (ssl == NULL) {
+        return NULL;
+    }
+    const jbyte* npn;
+    unsigned npnLength;
+    SSL_get0_alpn_selected(ssl, reinterpret_cast<const unsigned char**>(&npn), &npnLength);
+    if (npnLength == 0) {
+        return NULL;
+    }
+    jbyteArray result = env->NewByteArray(npnLength);
+    if (result != NULL) {
+        env->SetByteArrayRegion(result, 0, npnLength, npn);
+    }
+    return result;
+}
+
 #ifdef WITH_JNI_TRACE_KEYS
 static inline char hex_char(unsigned char in)
 {
 /**
  * Perform SSL handshake
  */
-static jlong NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jlong ssl_address,
-        jobject fdObject, jobject shc, jint timeout_millis, jboolean client_mode,
-        jbyteArray npnProtocols)
-{
+static jlong NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject,
+        jobject shc, jint timeout_millis, jboolean client_mode, jbyteArray npnProtocols,
+        jbyteArray alpnProtocols) {
     SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd=%p shc=%p timeout_millis=%d client_mode=%d npn=%p",
               ssl, fdObject, shc, timeout_millis, client_mode, npnProtocols);
         SSL_set_connect_state(ssl);
     } else {
         SSL_set_accept_state(ssl);
+        if (alpnProtocols != NULL) {
+            SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(ssl), alpn_select_callback, NULL);
+        }
     }
 
     ret = 0;
     while (appData->aliveAndKicking) {
         errno = 0;
 
-        if (!appData->setCallbackState(env, shc, fdObject, npnProtocols)) {
+        if (!appData->setCallbackState(env, shc, fdObject, npnProtocols, alpnProtocols)) {
             // SocketException thrown by NetFd.isClosed
             SSL_clear(ssl);
             JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setCallbackState => 0", ssl);
 
         unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
 
-        if (!appData->setCallbackState(env, shc, fdObject, NULL)) {
+        if (!appData->setCallbackState(env, shc, fdObject, NULL, NULL)) {
             MUTEX_UNLOCK(appData->mutex);
             return THROWN_EXCEPTION;
         }
 
         unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
 
-        if (!appData->setCallbackState(env, shc, fdObject, NULL)) {
+        if (!appData->setCallbackState(env, shc, fdObject, NULL, NULL)) {
             MUTEX_UNLOCK(appData->mutex);
             return THROWN_EXCEPTION;
         }
 
     AppData* appData = toAppData(ssl);
     if (appData != NULL) {
-        if (!appData->setCallbackState(env, shc, fdObject, NULL)) {
+        if (!appData->setCallbackState(env, shc, fdObject, NULL, NULL)) {
             // SocketException thrown by NetFd.isClosed
             SSL_clear(ssl);
             freeOpenSslErrorState();
     NATIVE_METHOD(NativeCrypto, SSL_set_session_creation_enabled, "(JZ)V"),
     NATIVE_METHOD(NativeCrypto, SSL_set_tlsext_host_name, "(JLjava/lang/String;)V"),
     NATIVE_METHOD(NativeCrypto, SSL_get_servername, "(J)Ljava/lang/String;"),
-    NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "IZ[B)I"),
+    NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "IZ[B[B)I"),
     NATIVE_METHOD(NativeCrypto, SSL_renegotiate, "(J)V"),
     NATIVE_METHOD(NativeCrypto, SSL_get_certificate, "(J)[[B"),
     NATIVE_METHOD(NativeCrypto, SSL_get_peer_cert_chain, "(J)[[B"),
     NATIVE_METHOD(NativeCrypto, SSL_CTX_enable_npn, "(J)V"),
     NATIVE_METHOD(NativeCrypto, SSL_CTX_disable_npn, "(J)V"),
     NATIVE_METHOD(NativeCrypto, SSL_get_npn_negotiated_protocol, "(J)[B"),
+    NATIVE_METHOD(NativeCrypto, SSL_CTX_set_alpn_protos, "(J[B)I"),
+    NATIVE_METHOD(NativeCrypto, SSL_get0_alpn_selected, "(J)[B"),
     NATIVE_METHOD(NativeCrypto, ERR_peek_last_error, "()J"),
 };
 

crypto/src/test/java/org/conscrypt/NativeCryptoTest.java

     }
 
     public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener,
-            final int timeout, final boolean client, final Hooks hooks, final byte[] npnProtocols) {
+            final int timeout, final boolean client, final Hooks hooks, final byte[] npnProtocols,
+            final byte[] alpnProtocols) {
         ExecutorService executor = Executors.newSingleThreadExecutor();
         Future<TestSSLHandshakeCallbacks> future = executor.submit(
                 new Callable<TestSSLHandshakeCallbacks>() {
                 long session = NULL;
                 try {
                     session = NativeCrypto.SSL_do_handshake(s, fd, callback, timeout, client,
-                                                            npnProtocols);
+                                                            npnProtocols, alpnProtocols);
                     if (DEBUG) {
                         System.out.println("ssl=0x" + Long.toString(s, 16)
                                            + " handshake"
 
     public void test_SSL_do_handshake_NULL_SSL() throws Exception {
         try {
-            NativeCrypto.SSL_do_handshake(NULL, null, null, 0, false, null);
+            NativeCrypto.SSL_do_handshake(NULL, null, null, 0, false, null, null);
             fail();
         } catch (NullPointerException expected) {
         }
         long s = NativeCrypto.SSL_new(c);
 
         try {
-            NativeCrypto.SSL_do_handshake(s, null, null, 0, true, null);
+            NativeCrypto.SSL_do_handshake(s, null, null, 0, true, null, null);
             fail();
         } catch (NullPointerException expected) {
         }
 
         try {
-            NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0, true, null);
+            NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0, true, null, null);
             fail();
         } catch (NullPointerException expected) {
         }
         final ServerSocket listener = new ServerSocket(0);
         Hooks cHooks = new Hooks();
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
                 return s;
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
                     return s;
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
+                    null);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
                 }
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         try {
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         } catch (ExecutionException e) {
         try {
             Hooks cHooks = new Hooks();
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks, null,
+                    null);
             serverSocket = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         try {
             Hooks cHooks = new Hooks();
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks, null, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks, null, null);
             clientSocket = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
         ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
         sHooks.channelIdEnabled = true;
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
         cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
         ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
         sHooks.channelIdEnabled = false;
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
         cHooks.channelIdPrivateKey = null;
         ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
         sHooks.channelIdEnabled = true;
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         assertTrue(clientCallback.verifyCertificateChainCalled);
                     }
                 };
                 Future<TestSSLHandshakeCallbacks> client
-                        = handshake(listener, 0, true, cHooks, null);
+                        = handshake(listener, 0, true, cHooks, null, null);
                 Future<TestSSLHandshakeCallbacks> server
-                        = handshake(listener, 0, false, sHooks, null);
+                        = handshake(listener, 0, false, sHooks, null, null);
                 client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
                 server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             }
                     }
                 };
                 Future<TestSSLHandshakeCallbacks> client
-                        = handshake(listener, 0, true, cHooks, null);
+                        = handshake(listener, 0, true, cHooks, null, null);
                 Future<TestSSLHandshakeCallbacks> server
-                        = handshake(listener, 0, false, sHooks, null);
+                        = handshake(listener, 0, false, sHooks, null, null);
                 client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
                 server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             }
                 }
             };
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
+                    null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
                     return s;
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
+                    null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
 
         ServerSocket listener = new ServerSocket(0);
         Future<TestSSLHandshakeCallbacks> client
-                = handshake(listener, 0, true, cHooks, clientNpnProtocols);
+                = handshake(listener, 0, true, cHooks, clientNpnProtocols, null);
         Future<TestSSLHandshakeCallbacks> server
-                = handshake(listener, 0, false, sHooks, serverNpnProtocols);
+                = handshake(listener, 0, false, sHooks, serverNpnProtocols, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    public void test_SSL_AlpnNegotiateSuccess() throws Exception {
+        final byte[] clientAlpnProtocols = new byte[] {
+                8, 'h', 't', 't', 'p', '/', '1', '.', '1',
+                3, 'f', 'o', 'o',
+                6, 's', 'p', 'd', 'y', '/', '2',
+        };
+        final byte[] serverAlpnProtocols = new byte[] {
+                6, 's', 'p', 'd', 'y', '/', '2',
+                3, 'f', 'o', 'o',
+                3, 'b', 'a', 'r',
+        };
+
+        Hooks cHooks = new Hooks() {
+            @Override public long beforeHandshake(long context) throws SSLException {
+                NativeCrypto.SSL_CTX_set_alpn_protos(context, clientAlpnProtocols);
+                return super.beforeHandshake(context);
+            }
+            @Override public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.SSL_get0_alpn_selected(ssl);
+                assertEquals("spdy/2", new String(negotiated));
+                /*
+                 * There is no callback on the client, so we can't enable
+                 * cut-through
+                 */
+                assertEquals("ALPN should not enable cutthrough on the client", 0,
+                        NativeCrypto.SSL_get_mode(ssl) & SSL_MODE_HANDSHAKE_CUTTHROUGH);
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override public void afterHandshake(long session, long ssl, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.SSL_get0_alpn_selected(ssl);
+                assertEquals("spdy/2", new String(negotiated));
+                assertEquals("ALPN should not enable cutthrough on the server",
+                        0, NativeCrypto.SSL_get_mode(ssl) & SSL_MODE_HANDSHAKE_CUTTHROUGH);
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ServerSocket listener = new ServerSocket(0);
+        Future<TestSSLHandshakeCallbacks> client
+                = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server
+                = handshake(listener, 0, false, sHooks, null, serverAlpnProtocols);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
                     super.afterHandshake(session, s, c, sock, fd, callback);
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
+                    null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         }
                     super.afterHandshake(session, s, c, sock, fd, callback);
                 }
             };
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
+                    null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             fail();
         } catch (ExecutionException expected) {
                 super.afterHandshake(session, s, c, sock, fd, callback);
             }
         };
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
                 }
             };
             Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
+                    null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
+                    null);
             client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
             server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         }
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
             }
         };
         Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
-        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
-        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
         client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
     }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.