NativeCrypto: Add TLS SCT extension support.

Change-Id: I438b23ecb86340f837f62359b342637966b81512
diff --git a/src/main/java/org/conscrypt/NativeCrypto.java b/src/main/java/org/conscrypt/NativeCrypto.java
index 1868278..05a6b36 100644
--- a/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/src/main/java/org/conscrypt/NativeCrypto.java
@@ -848,6 +848,12 @@
 
     public static native long SSL_clear_options(long ssl, long options);
 
+    public static native void SSL_enable_signed_cert_timestamps(long ssl);
+
+    public static native byte[] SSL_get_signed_cert_timestamp_list(long ssl);
+
+    public static native void SSL_CTX_set_signed_cert_timestamp_list(long ssl, byte[] list);
+
     public static native void SSL_enable_ocsp_stapling(long ssl);
 
     public static native byte[] SSL_get_ocsp_response(long ssl);
diff --git a/src/main/native/org_conscrypt_NativeCrypto.cpp b/src/main/native/org_conscrypt_NativeCrypto.cpp
index 62eb302..e6c6458 100644
--- a/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -8684,6 +8684,83 @@
 }
 
 
+/**
+ * public static native void SSL_enable_signed_cert_timestamps(long ssl);
+ */
+static void NativeCrypto_SSL_enable_signed_cert_timestamps(JNIEnv *env, jclass,
+        jlong ssl_address) {
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_enable_signed_cert_timestamps", ssl);
+    if (ssl == NULL) {
+        return;
+    }
+
+#if defined(OPENSSL_IS_BORINGSSL)
+    SSL_enable_signed_cert_timestamps(ssl);
+#endif
+}
+
+/**
+ * public static native byte[] SSL_get_signed_cert_timestamp_list(long ssl);
+ */
+static jbyteArray NativeCrypto_SSL_get_signed_cert_timestamp_list(JNIEnv *env, jclass,
+        jlong ssl_address) {
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_get_signed_cert_timestamp_list", ssl);
+    if (ssl == NULL)  {
+        return NULL;
+    }
+
+#if defined(OPENSSL_IS_BORINGSSL)
+    const uint8_t *data;
+    size_t data_len;
+    SSL_get0_signed_cert_timestamp_list(ssl, &data, &data_len);
+
+    if (data_len == 0) {
+        JNI_TRACE("NativeCrypto_SSL_get_signed_cert_timestamp_list(%p) => NULL",
+                ssl);
+        return NULL;
+    }
+
+    jbyteArray result = env->NewByteArray(data_len);
+    if (result != NULL) {
+        env->SetByteArrayRegion(result, 0, data_len, (const jbyte*)data);
+    }
+    return result;
+#else
+    return NULL;
+#endif
+}
+
+/*
+ * public static native void SSL_CTX_set_signed_cert_timestamp_list(long ssl, byte[] response);
+ */
+static void NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list(JNIEnv *env, jclass,
+        jlong ssl_ctx_address, jbyteArray list) {
+    SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+    JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list", ssl_ctx);
+    if (ssl_ctx == NULL)  {
+        return;
+    }
+
+    ScopedByteArrayRO listBytes(env, list);
+    if (listBytes.get() == NULL) {
+        JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list =>"
+                  " list == NULL", ssl_ctx);
+        return;
+    }
+
+#if defined(OPENSSL_IS_BORINGSSL)
+    if (!SSL_CTX_set_signed_cert_timestamp_list(ssl_ctx,
+                reinterpret_cast<const uint8_t *>(listBytes.get()),
+                listBytes.size())) {
+        JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list => fail", ssl_ctx);
+    } else {
+        JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_signed_cert_timestamp_list => ok", ssl_ctx);
+    }
+#endif
+}
+
 /*
  * public static native void SSL_enable_ocsp_stapling(long ssl);
  */
@@ -11237,6 +11314,9 @@
     NATIVE_METHOD(NativeCrypto, SSL_get_options, "(J)J"),
     NATIVE_METHOD(NativeCrypto, SSL_set_options, "(JJ)J"),
     NATIVE_METHOD(NativeCrypto, SSL_clear_options, "(JJ)J"),
+    NATIVE_METHOD(NativeCrypto, SSL_enable_signed_cert_timestamps, "(J)V"),
+    NATIVE_METHOD(NativeCrypto, SSL_get_signed_cert_timestamp_list, "(J)[B"),
+    NATIVE_METHOD(NativeCrypto, SSL_CTX_set_signed_cert_timestamp_list, "(J[B)V"),
     NATIVE_METHOD(NativeCrypto, SSL_enable_ocsp_stapling, "(J)V"),
     NATIVE_METHOD(NativeCrypto, SSL_get_ocsp_response, "(J)[B"),
     NATIVE_METHOD(NativeCrypto, SSL_CTX_set_ocsp_response, "(J[B)V"),
diff --git a/src/test/java/org/conscrypt/NativeCryptoTest.java b/src/test/java/org/conscrypt/NativeCryptoTest.java
index 85e51d8..b8f0bf9 100644
--- a/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -1551,6 +1551,49 @@
         assertTrue(serverCallback.handshakeCompletedCalled);
     }
 
+    public void test_SSL_do_handshake_with_sct_extension() throws Exception {
+        // This is only implemented for BoringSSL
+        if (!NativeCrypto.isBoringSSL) {
+            return;
+        }
+
+        final byte[] SCT_TEST_DATA = new byte[] { 1, 2, 3, 4};
+
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_enable_signed_cert_timestamps(s);
+                return s;
+            }
+
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                assertEqualByteArrays(SCT_TEST_DATA,
+                        NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                NativeCrypto.SSL_CTX_set_signed_cert_timestamp_list(c, SCT_TEST_DATA);
+                return super.beforeHandshake(c);
+            }
+        };
+
+        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.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
     public void test_SSL_use_psk_identity_hint() throws Exception {
         long c = NativeCrypto.SSL_CTX_new();
         long s = NativeCrypto.SSL_new(c);