diff --git a/.gitignore b/.gitignore
index 20f00a3..236597e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,5 @@ m4
 
 proxy/libproxy/glib-pacrunner
 proxy/libproxy/org.gtk.GLib.PACRunner.service
+
+/tls/tests/tls
diff --git a/Makefile.am b/Makefile.am
index 2c34280..41c7a60 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,6 +17,8 @@ if HAVE_GNUTLS
 SUBDIRS += tls/gnutls
 endif
 
+SUBDIRS += tls/tests
+
 install-exec-hook:
 	if test -n "$(GIO_QUERYMODULES)" -a -z "$(DESTDIR)"; then \
 		$(GIO_QUERYMODULES) $(GIO_MODULE_DIR) ;           \
diff --git a/configure.ac b/configure.ac
index 697b467..a355c65 100644
--- a/configure.ac
+++ b/configure.ac
@@ -157,8 +157,9 @@ AC_CONFIG_FILES([Makefile
                  po/Makefile.in po/Makefile
                  proxy/libproxy/Makefile
                  proxy/gnome/Makefile
-		 tls/gnutls/Makefile
-		])
+                 tls/gnutls/Makefile
+                 tls/tests/Makefile
+                ])
 AC_OUTPUT
 
 echo ""
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
index a206b08..f9f1965 100644
--- a/tls/gnutls/Makefile.am
+++ b/tls/gnutls/Makefile.am
@@ -28,6 +28,10 @@ libgiognutls_la_SOURCES = 		\
 	gtlsclientconnection-gnutls.h	\
 	gtlsconnection-gnutls.c		\
 	gtlsconnection-gnutls.h		\
+	gtlsdatabase-gnutls.c		\
+	gtlsdatabase-gnutls.h		\
+	gtlsfiledatabase-gnutls.c	\
+	gtlsfiledatabase-gnutls.h	\
 	gtlsinputstream-gnutls.c	\
 	gtlsinputstream-gnutls.h	\
 	gtlsoutputstream-gnutls.c	\
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index da15b01..75b3e93 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -32,8 +32,15 @@
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
 #include "gtlsclientconnection-gnutls.h"
+#include "gtlsfiledatabase-gnutls.h"
 #include "gtlsserverconnection-gnutls.h"
 
+struct _GTlsBackendGnutlsPrivate
+{
+  GMutex *mutex;
+  GTlsDatabase *default_database;
+};
+
 static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
 
 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
@@ -120,95 +127,81 @@ g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
    * g_io_modules_scan_all_in_directory()).
    */
   g_once (&gnutls_inited, gtls_gnutls_init, NULL);
+
+  backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsPrivate);
+  backend->priv->mutex = g_mutex_new ();
 }
 
 static void
-g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
+g_tls_backend_gnutls_finalize (GObject *object)
 {
+  GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
+
+  if (backend->priv->default_database)
+    g_object_unref (backend->priv->default_database);
+  g_mutex_free (backend->priv->mutex);
+
+  G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
 }
 
 static void
-g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
+g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
+  gobject_class->finalize = g_tls_backend_gnutls_finalize;
+  g_type_class_add_private (backend_class, sizeof (GTlsBackendGnutlsPrivate));
 }
 
 static void
-g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
+g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
 {
-  iface->get_certificate_type       = g_tls_certificate_gnutls_get_type;
-  iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
-  iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
 }
 
-#ifdef GTLS_SYSTEM_CA_FILE
-/* Parsing the system CA list takes a noticeable amount of time.
- * So we only do it once, and only when we actually need to see it.
- */
-static const GList *
-get_ca_lists (gnutls_x509_crt_t **cas,
-	      int                *num_cas)
+static GTlsDatabase*
+g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
 {
-  static gnutls_x509_crt_t *ca_list_gnutls;
-  static int ca_list_length;
-  static GList *ca_list;
+  GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
+  const gchar *anchor_file = NULL;
+  GTlsDatabase *result;
+  GError *error = NULL;
 
-  if (g_once_init_enter ((volatile gsize *)&ca_list_gnutls))
-    {
-      GError *error = NULL;
-      gnutls_x509_crt_t *x509_crts;
-      GList *c;
-      int i;
+  g_mutex_lock (self->priv->mutex);
 
-      ca_list = g_tls_certificate_list_new_from_file (GTLS_SYSTEM_CA_FILE, &error);
+  if (self->priv->default_database)
+    {
+      result = g_object_ref (self->priv->default_database);
+    }
+  else
+    {
+#ifdef GTLS_SYSTEM_CA_FILE
+      anchor_file = GTLS_SYSTEM_CA_FILE;
+#endif
+      result = g_tls_file_database_new (anchor_file, &error);
       if (error)
-	{
-	  g_warning ("Failed to read system CA file %s: %s.",
-		     GTLS_SYSTEM_CA_FILE, error->message);
-	  g_error_free (error);
-	  /* Note that this is not a security problem, since if
-	   * G_TLS_VALIDATE_CA is set, then this just means validation
-	   * will always fail, and if it isn't set, then it doesn't
-	   * matter that we couldn't read the CAs.
-	   */
-	}
-
-      ca_list_length = g_list_length (ca_list);
-      x509_crts = g_new (gnutls_x509_crt_t, ca_list_length);
-      for (c = ca_list, i = 0; c; c = c->next, i++)
-	x509_crts[i] = g_tls_certificate_gnutls_get_cert (c->data);
-
-      g_once_init_leave ((volatile gsize *)&ca_list_gnutls, GPOINTER_TO_SIZE (x509_crts));
+        {
+          g_warning ("couldn't load TLS file database: %s",
+                     error->message);
+          g_clear_error (&error);
+        }
+      else
+        {
+          self->priv->default_database = g_object_ref (result);
+        }
     }
 
-  if (cas)
-    *cas = ca_list_gnutls;
-  if (num_cas)
-    *num_cas = ca_list_length;
-  
-  return ca_list;
-}
-#endif
+  g_mutex_unlock (self->priv->mutex);
 
-const GList *
-g_tls_backend_gnutls_get_system_ca_list_gtls (void)
-{
-#ifdef GTLS_SYSTEM_CA_FILE
-  return get_ca_lists (NULL, NULL);
-#else
-  return NULL;
-#endif
+  return result;
 }
 
-void
-g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
-						int                *num_cas)
+static void
+g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
 {
-#ifdef GTLS_SYSTEM_CA_FILE
-  get_ca_lists (cas, num_cas);
-#else
-  *cas = NULL;
-  *num_cas = 0;
-#endif
+  iface->get_certificate_type       = g_tls_certificate_gnutls_get_type;
+  iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
+  iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
+  iface->get_file_database_type =     g_tls_file_database_gnutls_get_type;
+  iface->get_default_database =       g_tls_backend_gnutls_get_default_database;
 }
 
 /* Session cache support; all the details are sort of arbitrary. Note
diff --git a/tls/gnutls/gtlsbackend-gnutls.h b/tls/gnutls/gtlsbackend-gnutls.h
index 97ebd90..6ab0344 100644
--- a/tls/gnutls/gtlsbackend-gnutls.h
+++ b/tls/gnutls/gtlsbackend-gnutls.h
@@ -25,8 +25,9 @@ G_BEGIN_DECLS
 #define G_IS_TLS_BACKEND_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_BACKEND_GNUTLS))
 #define G_TLS_BACKEND_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsClass))
 
-typedef struct _GTlsBackendGnutlsClass GTlsBackendGnutlsClass;
-typedef struct _GTlsBackendGnutls      GTlsBackendGnutls;
+typedef struct _GTlsBackendGnutls        GTlsBackendGnutls;
+typedef struct _GTlsBackendGnutlsClass   GTlsBackendGnutlsClass;
+typedef struct _GTlsBackendGnutlsPrivate GTlsBackendGnutlsPrivate;
 
 struct _GTlsBackendGnutlsClass
 {
@@ -36,15 +37,12 @@ struct _GTlsBackendGnutlsClass
 struct _GTlsBackendGnutls
 {
   GObject parent_instance;
+  GTlsBackendGnutlsPrivate *priv;
 };
 
 GType g_tls_backend_gnutls_get_type (void) G_GNUC_CONST;
 void  g_tls_backend_gnutls_register (GIOModule *module);
 
-const GList *g_tls_backend_gnutls_get_system_ca_list_gtls   (void) G_GNUC_CONST;
-void         g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
-							     int                *num_cas);
-
 void         g_tls_backend_gnutls_cache_session_data        (const gchar *session_id,
 							     guchar      *session_data,
 							     gsize        session_data_length);
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index f4269a7..956235e 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -498,3 +498,18 @@ g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
 
   return G_TLS_CERTIFICATE_BAD_IDENTITY;
 }
+
+void
+g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls,
+                                     GTlsCertificateGnutls *issuer)
+{
+  g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
+  g_return_if_fail (!issuer || G_IS_TLS_CERTIFICATE_GNUTLS (issuer));
+
+  if (issuer)
+    g_object_ref (issuer);
+  if (gnutls->priv->issuer)
+    g_object_unref (gnutls->priv->issuer);
+  gnutls->priv->issuer = issuer;
+  g_object_notify (G_OBJECT (gnutls), "issuer");
+}
diff --git a/tls/gnutls/gtlscertificate-gnutls.h b/tls/gnutls/gtlscertificate-gnutls.h
index a94117a..201dbb4 100644
--- a/tls/gnutls/gtlscertificate-gnutls.h
+++ b/tls/gnutls/gtlscertificate-gnutls.h
@@ -56,6 +56,10 @@ GTlsCertificateFlags         g_tls_certificate_gnutls_verify_identity (GTlsCerti
 
 GTlsCertificateFlags         g_tls_certificate_gnutls_convert_flags   (guint                  gnutls_flags);
 
+void                         g_tls_certificate_gnutls_set_issuer      (GTlsCertificateGnutls *gnutls,
+                                                                       GTlsCertificateGnutls *issuer);
+
+GTlsCertificateGnutls*       g_tls_certificate_gnutls_steal_issuer    (GTlsCertificateGnutls *gnutls);
 
 G_END_DECLS
 
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 2710e9c..b6fc2fb 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -327,12 +327,30 @@ g_tls_client_connection_gnutls_verify_peer (GTlsConnectionGnutls  *conn_gnutls,
 					    GTlsCertificateFlags  *errors)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn_gnutls);
+  GTlsDatabase *database;
   gboolean accepted;
+  GError *error = NULL;
 
-  if (gnutls->priv->server_identity)
+  database = g_tls_connection_get_database (G_TLS_CONNECTION (conn_gnutls));
+  if (database == NULL)
+    {
+      *errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+      *errors |= g_tls_certificate_verify (peer_certificate, gnutls->priv->server_identity, NULL);
+    }
+  else
     {
-      *errors |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (peer_certificate),
-							   gnutls->priv->server_identity);
+      *errors |= g_tls_database_verify_chain (database, peer_certificate,
+                                              G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                              gnutls->priv->server_identity,
+                                              g_tls_connection_get_interaction (G_TLS_CONNECTION (gnutls)),
+                                              G_TLS_DATABASE_VERIFY_NONE,
+                                              NULL, &error);
+      if (error)
+        {
+          g_warning ("failure verifying certificate chain: %s",
+                     error->message);
+          g_clear_error (&error);
+        }
     }
 
   if (*errors & gnutls->priv->validation_flags)
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 5b15910..48e05d6 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -99,7 +99,9 @@ enum
   PROP_REQUIRE_CLOSE_NOTIFY,
   PROP_REHANDSHAKE_MODE,
   PROP_USE_SYSTEM_CERTDB,
+  PROP_DATABASE,
   PROP_CERTIFICATE,
+  PROP_INTERACTION,
   PROP_PEER_CERTIFICATE,
   PROP_PEER_CERTIFICATE_ERRORS
 };
@@ -110,7 +112,6 @@ struct _GTlsConnectionGnutlsPrivate
   GPollableInputStream *base_istream;
   GPollableOutputStream *base_ostream;
 
-  GList *ca_list;
   gnutls_certificate_credentials creds;
   gnutls_session session;
 
@@ -118,13 +119,17 @@ struct _GTlsConnectionGnutlsPrivate
   GTlsCertificateFlags peer_certificate_errors;
   gboolean require_close_notify;
   GTlsRehandshakeMode rehandshake_mode;
-  gboolean use_system_certdb;
+  gboolean is_system_certdb;
+  GTlsDatabase *database;
+  gboolean database_is_unset;
   gboolean need_handshake, handshaking, ever_handshaked;
   gboolean closing;
 
   GInputStream *tls_istream;
   GOutputStream *tls_ostream;
 
+  GTlsInteraction *interaction;
+
   GError *error;
   GCancellable *cancellable;
   gboolean blocking, eof;
@@ -158,7 +163,9 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify");
   g_object_class_override_property (gobject_class, PROP_REHANDSHAKE_MODE, "rehandshake-mode");
   g_object_class_override_property (gobject_class, PROP_USE_SYSTEM_CERTDB, "use-system-certdb");
+  g_object_class_override_property (gobject_class, PROP_DATABASE, "database");
   g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
+  g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
 }
@@ -179,6 +186,9 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
 				       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
 
   gnutls->priv->need_handshake = TRUE;
+
+  gnutls->priv->database_is_unset = TRUE;
+  gnutls->priv->is_system_certdb = TRUE;
 }
 
 static gnutls_priority_t priorities[2][2];
@@ -274,11 +284,15 @@ g_tls_connection_gnutls_finalize (GObject *object)
   if (connection->priv->creds)
     gnutls_certificate_free_credentials (connection->priv->creds);
 
+  if (connection->priv->database)
+    g_object_unref (connection->priv->database);
   if (connection->priv->certificate)
     g_object_unref (connection->priv->certificate);
   if (connection->priv->peer_certificate)
     g_object_unref (connection->priv->peer_certificate);
 
+  g_clear_object (&connection->priv->interaction);
+
   if (connection->priv->error)
     g_error_free (connection->priv->error);
 
@@ -292,6 +306,7 @@ g_tls_connection_gnutls_get_property (GObject    *object,
 				      GParamSpec *pspec)
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
+  GTlsBackend *backend;
 
   switch (prop_id)
     {
@@ -308,13 +323,27 @@ g_tls_connection_gnutls_get_property (GObject    *object,
       break;
 
     case PROP_USE_SYSTEM_CERTDB:
-      g_value_set_boolean (value, gnutls->priv->use_system_certdb);
+      g_value_set_boolean (value, gnutls->priv->is_system_certdb);
+      break;
+
+    case PROP_DATABASE:
+      if (gnutls->priv->database_is_unset)
+        {
+          backend = g_tls_backend_get_default ();
+          gnutls->priv->database =  g_tls_backend_get_default_database (backend);
+          gnutls->priv->database_is_unset = FALSE;
+        }
+      g_value_set_object (value, gnutls->priv->database);
       break;
 
     case PROP_CERTIFICATE:
       g_value_set_object (value, gnutls->priv->certificate);
       break;
 
+    case PROP_INTERACTION:
+      g_value_set_object (value, gnutls->priv->interaction);
+      break;
+
     case PROP_PEER_CERTIFICATE:
       g_value_set_object (value, gnutls->priv->peer_certificate);
       break;
@@ -337,6 +366,8 @@ g_tls_connection_gnutls_set_property (GObject      *object,
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
   GInputStream *istream;
   GOutputStream *ostream;
+  gboolean system_certdb;
+  GTlsBackend *backend;
 
   switch (prop_id)
     {
@@ -371,17 +402,24 @@ g_tls_connection_gnutls_set_property (GObject      *object,
       break;
 
     case PROP_USE_SYSTEM_CERTDB:
-      gnutls->priv->use_system_certdb = g_value_get_boolean (value);
-
-      gnutls_certificate_free_cas (gnutls->priv->creds);
-      if (gnutls->priv->use_system_certdb)
-	{
-	  gnutls_x509_crt_t *cas;
-	  int num_cas;
+      system_certdb = g_value_get_boolean (value);
+      if (system_certdb != gnutls->priv->is_system_certdb)
+        {
+          g_clear_object (&gnutls->priv->database);
+          if (system_certdb)
+            {
+              backend = g_tls_backend_get_default ();
+              gnutls->priv->database = g_tls_backend_get_default_database (backend);
+            }
+          gnutls->priv->is_system_certdb = system_certdb;
+        }
+      break;
 
-	  g_tls_backend_gnutls_get_system_ca_list_gnutls (&cas, &num_cas);
-	  gnutls_certificate_set_x509_trust (gnutls->priv->creds, cas, num_cas);
-	}
+    case PROP_DATABASE:
+      g_clear_object (&gnutls->priv->database);
+      gnutls->priv->database = g_value_dup_object (value);
+      gnutls->priv->is_system_certdb = FALSE;
+      gnutls->priv->database_is_unset = FALSE;
       break;
 
     case PROP_CERTIFICATE:
@@ -390,6 +428,11 @@ g_tls_connection_gnutls_set_property (GObject      *object,
       gnutls->priv->certificate = g_value_dup_object (value);
       break;
 
+    case PROP_INTERACTION:
+      g_clear_object (&gnutls->priv->interaction);
+      gnutls->priv->interaction = g_value_dup_object (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -860,20 +903,6 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
 
   if (peer_certificate)
     {
-      int status;
-
-      status = gnutls_certificate_verify_peers (gnutls->priv->session);
-      peer_certificate_errors = g_tls_certificate_gnutls_convert_flags (status);
-      if (peer_certificate_errors)
-	{
-	  /* gnutls_certificate_verify_peers() bails out on the first
-	   * error, which may be G_TLS_CERTIFICATE_UNKNOWN_CA, but the
-	   * caller may be planning to check that part themselves. So
-	   * call g_tls_certificate_verify() to get any other errors.
-	   */
-	  peer_certificate_errors |= g_tls_certificate_verify (peer_certificate, NULL, NULL);
-	}
-
       if (!G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->verify_peer (gnutls, peer_certificate, &peer_certificate_errors))
 	{
 	  g_object_unref (peer_certificate);
diff --git a/tls/gnutls/gtlsdatabase-gnutls.c b/tls/gnutls/gtlsdatabase-gnutls.c
new file mode 100644
index 0000000..3e21052
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls.c
@@ -0,0 +1,314 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+#include "gtlscertificate-gnutls.h"
+
+#include <glib/gi18n-lib.h>
+
+G_DEFINE_ABSTRACT_TYPE (GTlsDatabaseGnutls, g_tls_database_gnutls, G_TYPE_TLS_DATABASE);
+
+enum {
+  STATUS_FAILURE,
+  STATUS_INCOMPLETE,
+  STATUS_SELFSIGNED,
+  STATUS_PINNED,
+  STATUS_ANCHORED,
+};
+
+static void
+g_tls_database_gnutls_init (GTlsDatabaseGnutls *self)
+{
+
+}
+
+static gboolean
+is_self_signed (GTlsCertificateGnutls *certificate)
+{
+  const gnutls_x509_crt_t cert = g_tls_certificate_gnutls_get_cert (certificate);
+  return (gnutls_x509_crt_check_issuer (cert, cert) > 0);
+}
+
+static gint
+build_certificate_chain (GTlsDatabaseGnutls      *self,
+                         GTlsCertificateGnutls   *chain,
+                         const gchar             *purpose,
+                         GSocketConnectable      *identity,
+                         GTlsInteraction         *interaction,
+                         GTlsDatabaseVerifyFlags  flags,
+                         GCancellable            *cancellable,
+                         GTlsCertificateGnutls  **anchor,
+                         GError                 **error)
+{
+
+  GTlsCertificateGnutls *certificate;
+  GTlsCertificate *issuer;
+
+  g_assert (anchor);
+  g_assert (chain);
+  g_assert (purpose);
+  g_assert (error);
+  g_assert (!*error);
+
+  /*
+   * Remember that the first certificate never changes in the chain.
+   * When we find a self-signed, pinned or anchored certificate, all
+   * issuers are truncated from the chain.
+   */
+
+  *anchor = NULL;
+  certificate = chain;
+
+  /* First check for pinned certificate */
+  if (g_tls_database_gnutls_lookup_assertion (self, certificate,
+                                              G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+                                              purpose, identity, cancellable, error))
+    {
+      g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+      return STATUS_PINNED;
+    }
+  else if(*error)
+    {
+      return STATUS_FAILURE;
+    }
+
+  for (;;)
+    {
+      /* Was the last certificate self-signed? */
+      if (is_self_signed (certificate))
+        {
+          g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+          return STATUS_SELFSIGNED;
+        }
+
+      /* Bring over the next certificate in the chain */
+      issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+      if (issuer)
+        {
+          g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+        }
+
+      /* Search for the next certificate in chain */
+      else
+        {
+          issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+                                                             G_TLS_CERTIFICATE (certificate),
+                                                             interaction,
+                                                             G_TLS_DATABASE_LOOKUP_NONE,
+                                                             cancellable, error);
+          if (*error)
+            return STATUS_FAILURE;
+          else if (!issuer)
+            return STATUS_INCOMPLETE;
+          g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+          g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
+        }
+
+      g_assert (issuer);
+      certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+
+      /* Now look up whether this certificate is an anchor */
+      if (g_tls_database_gnutls_lookup_assertion (self, certificate,
+                                                  G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+                                                  purpose, identity, cancellable, error))
+        {
+          g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+          *anchor = certificate;
+          return STATUS_ANCHORED;
+        }
+      else if (*error)
+        {
+          return STATUS_FAILURE;
+        }
+    }
+
+  g_assert_not_reached ();
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateGnutls *chain)
+{
+  GTlsCertificateFlags gtls_flags = 0;
+  gnutls_x509_crt_t cert;
+  time_t t, now;
+
+  now = time (NULL);
+  while (chain)
+    {
+      cert = g_tls_certificate_gnutls_get_cert (chain);
+      t = gnutls_x509_crt_get_activation_time (cert);
+      if (t == (time_t) -1 || t > now)
+        gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+      t = gnutls_x509_crt_get_expiration_time (cert);
+      if (t == (time_t) -1 || t < now)
+        gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+
+      chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
+                                        (G_TLS_CERTIFICATE (chain)));
+    }
+
+  return gtls_flags;
+}
+
+static void
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls    *chain,
+                                     gnutls_x509_crt_t       **gnutls_chain,
+                                     guint                    *gnutls_chain_length)
+{
+  GTlsCertificate *cert;
+  guint i;
+
+  g_assert (gnutls_chain);
+  g_assert (gnutls_chain_length);
+
+  for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
+      cert; cert = g_tls_certificate_get_issuer (cert))
+    ++(*gnutls_chain_length);
+
+  *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+
+  for (i = 0, cert = G_TLS_CERTIFICATE (chain);
+      cert; cert = g_tls_certificate_get_issuer (cert), ++i)
+    (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+
+  g_assert (i == *gnutls_chain_length);
+}
+
+static GTlsCertificateFlags
+g_tls_database_gnutls_verify_chain (GTlsDatabase           *database,
+                                    GTlsCertificate        *chain,
+                                    const gchar            *purpose,
+                                    GSocketConnectable     *identity,
+                                    GTlsInteraction        *interaction,
+                                    GTlsDatabaseVerifyFlags flags,
+                                    GCancellable           *cancellable,
+                                    GError                **error)
+{
+  GTlsDatabaseGnutls *self;
+  GTlsCertificateFlags result;
+  GError *err = NULL;
+  GTlsCertificateGnutls *anchor;
+  guint gnutls_result;
+  gnutls_x509_crt_t *certs, *anchors;
+  guint certs_length, anchors_length;
+  gint status, gerr;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
+                        G_TLS_CERTIFICATE_GENERIC_ERROR);
+
+  self = G_TLS_DATABASE_GNUTLS (database);
+  anchor = NULL;
+
+  status = build_certificate_chain (self, G_TLS_CERTIFICATE_GNUTLS (chain), purpose,
+                                    identity, interaction, flags, cancellable, &anchor, &err);
+  if (status == STATUS_FAILURE)
+    {
+      g_propagate_error (error, err);
+      return G_TLS_CERTIFICATE_GENERIC_ERROR;
+    }
+
+  /*
+   * A pinned certificate is verified on its own, without any further
+   * verification.
+   */
+  if (status == STATUS_PINNED)
+      return 0;
+
+  convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+                                       &certs, &certs_length);
+
+  if (anchor)
+    {
+      g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+      convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
+                                           &anchors, &anchors_length);
+    }
+  else
+    {
+      anchors = NULL;
+      anchors_length = 0;
+    }
+
+  gerr = gnutls_x509_crt_list_verify (certs, certs_length,
+                                      anchors, anchors_length,
+                                      NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+                                      &gnutls_result);
+
+  g_free (certs);
+  g_free (anchors);
+
+  if (gerr != 0)
+      return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+  result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
+
+  /*
+   * We have to check these ourselves since gnutls_x509_crt_list_verify
+   * won't bother if it gets an UNKNOWN_CA.
+   */
+  result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
+
+  if (identity)
+    result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+                                                        identity);
+
+  return result;
+}
+
+static void
+g_tls_database_gnutls_class_init (GTlsDatabaseGnutlsClass *klass)
+{
+  GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+  database_class->verify_chain = g_tls_database_gnutls_verify_chain;
+}
+
+gboolean
+g_tls_database_gnutls_lookup_assertion (GTlsDatabaseGnutls          *self,
+                                        GTlsCertificateGnutls       *certificate,
+                                        GTlsDatabaseGnutlsAssertion  assertion,
+                                        const gchar                 *purpose,
+                                        GSocketConnectable          *identity,
+                                        GCancellable                *cancellable,
+                                        GError                     **error)
+{
+  g_return_val_if_fail (G_IS_TLS_DATABASE_GNUTLS (self), FALSE);
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), FALSE);
+  g_return_val_if_fail (purpose, FALSE);
+  g_return_val_if_fail (!identity || G_IS_SOCKET_CONNECTABLE (identity), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+  g_return_val_if_fail (!error || !*error, FALSE);
+  g_return_val_if_fail (G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->lookup_assertion, FALSE);
+  return G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->lookup_assertion (self,
+                                                                   certificate,
+                                                                   assertion,
+                                                                   purpose,
+                                                                   identity,
+                                                                   cancellable,
+                                                                   error);
+}
diff --git a/tls/gnutls/gtlsdatabase-gnutls.h b/tls/gnutls/gtlsdatabase-gnutls.h
new file mode 100644
index 0000000..f4bf8a5
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls.h
@@ -0,0 +1,71 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Collabora, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#ifndef __G_TLS_DATABASE_GNUTLS_H__
+#define __G_TLS_DATABASE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+#include "gtlscertificate-gnutls.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+  G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE = 1,
+  G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE = 2,
+} GTlsDatabaseGnutlsAssertion;
+
+#define G_TYPE_TLS_DATABASE_GNUTLS            (g_tls_database_gnutls_get_type ())
+#define G_TLS_DATABASE_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutls))
+#define G_TLS_DATABASE_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutlsClass))
+#define G_IS_TLS_DATABASE_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_DATABASE_GNUTLS))
+#define G_IS_TLS_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_DATABASE_GNUTLS))
+#define G_TLS_DATABASE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutlsClass))
+
+typedef struct _GTlsDatabaseGnutlsPrivate                   GTlsDatabaseGnutlsPrivate;
+typedef struct _GTlsDatabaseGnutlsClass                     GTlsDatabaseGnutlsClass;
+typedef struct _GTlsDatabaseGnutls                          GTlsDatabaseGnutls;
+
+struct _GTlsDatabaseGnutlsClass
+{
+  GTlsDatabaseClass parent_class;
+
+  gboolean       (*lookup_assertion)      (GTlsDatabaseGnutls          *self,
+                                           GTlsCertificateGnutls       *certificate,
+                                           GTlsDatabaseGnutlsAssertion  assertion,
+                                           const gchar                 *purpose,
+                                           GSocketConnectable          *identity,
+                                           GCancellable                *cancellable,
+                                           GError                     **error);
+};
+
+struct _GTlsDatabaseGnutls
+{
+  GTlsDatabase parent_instance;
+  GTlsDatabaseGnutlsPrivate *priv;
+};
+
+GType          g_tls_database_gnutls_get_type              (void) G_GNUC_CONST;
+
+gboolean       g_tls_database_gnutls_lookup_assertion      (GTlsDatabaseGnutls          *self,
+                                                            GTlsCertificateGnutls       *certificate,
+                                                            GTlsDatabaseGnutlsAssertion  assertion,
+                                                            const gchar                 *purpose,
+                                                            GSocketConnectable          *identity,
+                                                            GCancellable                *cancellable,
+                                                            GError                     **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsfiledatabase-gnutls.c b/tls/gnutls/gtlsfiledatabase-gnutls.c
new file mode 100644
index 0000000..2f9f1f0
--- /dev/null
+++ b/tls/gnutls/gtlsfiledatabase-gnutls.c
@@ -0,0 +1,586 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include "gtlsfiledatabase-gnutls.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <gnutls/x509.h>
+
+#define HANDLE_PREFIX "gnutls-file-database:sha256:"
+#define DIGEST_TYPE G_CHECKSUM_SHA256
+
+static void g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface);
+
+static void g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsFileDatabaseGnutls, g_tls_file_database_gnutls, G_TYPE_TLS_DATABASE_GNUTLS,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_TLS_FILE_DATABASE,
+                                                g_tls_file_database_gnutls_file_database_interface_init);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_tls_file_database_gnutls_initable_interface_init);
+);
+
+enum
+{
+  PROP_0,
+  PROP_ANCHORS,
+};
+
+struct _GTlsFileDatabaseGnutlsPrivate
+{
+  /* read-only after construct */
+  gchar *anchor_filename;
+
+  /* protected by mutex */
+  GMutex *mutex;
+
+  /*
+   * These are hash tables of GByteArray -> GPtrArray<GByteArray>. The values of
+   * the ptr array are full DER encoded certificate values. The keys are byte
+   * arrays containing either subject DNs, issuer DNs, or digested DER
+   */
+  GHashTable *subjects;
+  GHashTable *issuers;
+  GHashTable *digests;
+};
+
+static guint
+byte_array_hash (gconstpointer v)
+{
+  const GByteArray *array = v;
+  const signed char *p;
+  guint32 h = 0;
+  gsize i;
+
+  g_assert (array);
+  g_assert (array->data);
+  p = (signed char*)array->data;
+
+  /* 31 bit hash function */
+  for (i = 0; i < array->len; ++i, ++p)
+    h = (h << 5) - h + *p;
+
+  return h;
+}
+
+static gboolean
+byte_array_equal (gconstpointer v1, gconstpointer v2)
+{
+  const GByteArray *array1 = v1;
+  const GByteArray *array2 = v2;
+
+  if (array1 == array2)
+    return TRUE;
+  if (!array1 || !array2)
+    return FALSE;
+
+  if (array1->len != array2->len)
+    return FALSE;
+
+  if (array1->data == array2->data)
+    return TRUE;
+  if (!array1->data || !array2->data)
+    return FALSE;
+
+  return (memcmp (array1->data, array2->data, array1->len) == 0) ? TRUE : FALSE;
+}
+
+static GHashTable *
+multi_byte_array_hash_new (void)
+{
+  return g_hash_table_new_full (byte_array_hash, byte_array_equal,
+                                (GDestroyNotify)g_byte_array_unref,
+                                (GDestroyNotify)g_ptr_array_unref);
+}
+
+static void
+multi_byte_array_hash_insert (GHashTable *table, GByteArray *key, GByteArray *value)
+{
+  GPtrArray *multi;
+
+  multi = g_hash_table_lookup (table, key);
+  if (multi == NULL)
+    {
+      multi = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
+      g_hash_table_insert (table, g_byte_array_ref (key), multi);
+    }
+  g_ptr_array_add (multi, g_byte_array_ref (value));
+}
+
+static GByteArray *
+multi_byte_array_hash_lookup_one (GHashTable *table, GByteArray *key)
+{
+  GPtrArray *multi;
+
+  multi = g_hash_table_lookup (table, key);
+  if (multi == NULL)
+    return NULL;
+
+  g_assert (multi->len > 0);
+  return multi->pdata[0];
+}
+
+static GPtrArray *
+multi_byte_array_hash_lookup_all (GHashTable *table, GByteArray *key)
+{
+  return g_hash_table_lookup (table, key);
+}
+
+static GByteArray *
+compute_digest_for_byte_array (GByteArray *input)
+{
+  GChecksum *checksum = g_checksum_new (DIGEST_TYPE);
+  GByteArray *digest;
+  gsize length;
+
+  g_checksum_reset (checksum);
+  g_checksum_update (checksum, input->data, input->len);
+  digest = g_byte_array_new ();
+  length = g_checksum_type_get_length (DIGEST_TYPE);
+  g_byte_array_set_size (digest, length);
+  g_checksum_get_digest (checksum, digest->data, &length);
+  g_assert (length == digest->len);
+
+  return digest;
+}
+
+static gboolean
+load_anchor_file (const gchar *filename,
+                  GHashTable  *subjects,
+                  GHashTable  *issuers,
+                  GHashTable  *digests,
+                  GError     **error)
+{
+  GList *list, *l;
+  gnutls_x509_crt_t cert;
+  gnutls_datum_t dn;
+  GByteArray *der;
+  GByteArray *digest;
+  GByteArray *subject;
+  GByteArray *issuer;
+  gint gerr;
+
+  g_assert (error);
+
+  list = g_tls_certificate_list_new_from_file (filename, error);
+  if (*error)
+    return FALSE;
+
+
+  for (l = list; l; l = g_list_next (l))
+    {
+      cert = g_tls_certificate_gnutls_get_cert (l->data);
+      gerr = gnutls_x509_crt_get_raw_dn (cert, &dn);
+      if (gerr < 0)
+        {
+          g_warning ("failed to get subject of anchor certificate: %s",
+                     gnutls_strerror (gerr));
+          continue;
+        }
+
+      subject = g_byte_array_new ();
+      g_byte_array_append (subject, dn.data, dn.size);
+      gnutls_free (dn.data);
+
+      gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+      if (gerr < 0)
+        {
+          g_warning ("failed to get subject of anchor certificate: %s",
+                     gnutls_strerror (gerr));
+          continue;
+        }
+
+      issuer = g_byte_array_new ();
+      g_byte_array_append (issuer, dn.data, dn.size);
+      gnutls_free (dn.data);
+
+      /* Dig out the full value of this certificate's DER encoding */
+      der = NULL;
+      g_object_get (l->data, "certificate", &der, NULL);
+      g_return_val_if_fail (der, FALSE);
+
+      digest = compute_digest_for_byte_array (der);
+
+      /* Three different ways of looking up same certificate */
+      multi_byte_array_hash_insert (subjects, subject, der);
+      multi_byte_array_hash_insert (issuers, issuer, der);
+      multi_byte_array_hash_insert (digests, digest, der);
+
+      g_byte_array_unref (der);
+      g_byte_array_unref (subject);
+      g_byte_array_unref (issuer);
+      g_byte_array_unref (digest);
+    }
+
+  return TRUE;
+}
+
+static void
+g_tls_file_database_gnutls_finalize (GObject *object)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+  if (self->priv->subjects)
+    g_hash_table_destroy (self->priv->subjects);
+  self->priv->subjects = NULL;
+
+  if (self->priv->issuers)
+    g_hash_table_destroy (self->priv->issuers);
+  self->priv->issuers = NULL;
+
+  if (self->priv->digests)
+    g_hash_table_destroy (self->priv->digests);
+  self->priv->digests = NULL;
+
+  g_free (self->priv->anchor_filename);
+  self->priv->anchor_filename = NULL;
+
+  g_mutex_free (self->priv->mutex);
+
+  G_OBJECT_CLASS (g_tls_file_database_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_file_database_gnutls_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ANCHORS:
+      g_value_set_string (value, self->priv->anchor_filename);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_file_database_gnutls_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ANCHORS:
+      self->priv->anchor_filename = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_file_database_gnutls_init (GTlsFileDatabaseGnutls *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                            G_TYPE_TLS_FILE_DATABASE_GNUTLS,
+                                            GTlsFileDatabaseGnutlsPrivate);
+  self->priv->mutex = g_mutex_new ();
+}
+
+static gchar*
+g_tls_file_database_gnutls_create_certificate_handle (GTlsDatabase            *database,
+                                                      GTlsCertificate         *certificate)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  GByteArray *der;
+  GByteArray *digest;
+  gchar *encoded;
+  gchar *handle;
+  gboolean contains;
+
+  g_object_get (certificate, "certificate", &der, NULL);
+  g_return_val_if_fail (der, FALSE);
+
+  digest = compute_digest_for_byte_array (der);
+  g_object_unref (der);
+
+  g_mutex_lock (self->priv->mutex);
+  contains = multi_byte_array_hash_lookup_one (self->priv->digests, digest) ? TRUE : FALSE;
+  g_mutex_unlock (self->priv->mutex);
+
+  /* Certificate is not in the database */
+  if (!contains)
+    {
+      g_byte_array_unref (digest);
+      return NULL;
+    }
+
+  encoded = g_base64_encode (digest->data, digest->len);
+  handle = g_strconcat (HANDLE_PREFIX, encoded, NULL);
+  g_free (encoded);
+
+  return handle;
+}
+
+static GTlsCertificate*
+g_tls_file_database_gnutls_lookup_certificate_for_handle (GTlsDatabase            *database,
+                                                          const gchar             *handle,
+                                                          GTlsInteraction         *interaction,
+                                                          GTlsDatabaseLookupFlags  flags,
+                                                          GCancellable            *cancellable,
+                                                          GError                 **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  GByteArray *digest;
+  GByteArray *der;
+  gnutls_datum_t datum;
+  guchar *raw;
+  gsize length;
+
+  if (!g_str_has_prefix (handle, HANDLE_PREFIX))
+    return NULL;
+  handle += strlen (HANDLE_PREFIX);
+
+  raw = g_base64_decode (handle, &length);
+  if (raw == NULL)
+    return NULL;
+
+  digest = g_byte_array_new ();
+  g_byte_array_append (digest, raw, length);
+  g_free (raw);
+
+  g_mutex_lock (self->priv->mutex);
+  der = multi_byte_array_hash_lookup_one (self->priv->digests, digest);
+  g_mutex_unlock (self->priv->mutex);
+
+  g_byte_array_unref (digest);
+  datum.data = der->data;
+  datum.size = der->len;
+
+  return g_tls_certificate_gnutls_new (&datum, NULL);
+}
+
+static gboolean
+g_tls_file_database_gnutls_lookup_assertion (GTlsDatabaseGnutls          *database,
+                                             GTlsCertificateGnutls       *certificate,
+                                             GTlsDatabaseGnutlsAssertion  assertion,
+                                             const gchar                 *purpose,
+                                             GSocketConnectable          *identity,
+                                             GCancellable                *cancellable,
+                                             GError                     **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  GByteArray *der = NULL;
+  GByteArray *digest;
+  gboolean contains;
+
+  /* We only have anchored certificate assertions here */
+  if (assertion != G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
+    return FALSE;
+
+  /*
+   * TODO: We should be parsing any Extended Key Usage attributes and
+   * comparing them to the purpose.
+   */
+
+  g_object_get (certificate, "certificate", &der, NULL);
+  g_return_val_if_fail (der, FALSE);
+
+  digest = compute_digest_for_byte_array (der);
+
+  g_mutex_lock (self->priv->mutex);
+  contains = multi_byte_array_hash_lookup_one (self->priv->digests, digest) ? TRUE : FALSE;
+  g_mutex_unlock (self->priv->mutex);
+
+  g_byte_array_unref (digest);
+  g_byte_array_unref (der);
+
+  /* All certificates in our file are anchored certificates */
+  return contains;
+}
+
+static GTlsCertificate*
+g_tls_file_database_gnutls_lookup_certificate_issuer (GTlsDatabase           *database,
+                                                      GTlsCertificate        *certificate,
+                                                      GTlsInteraction        *interaction,
+                                                      GTlsDatabaseLookupFlags flags,
+                                                      GCancellable           *cancellable,
+                                                      GError                **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  gnutls_datum_t dn = { NULL, 0 };
+  GByteArray *subject, *der;
+  gnutls_datum_t datum;
+  GTlsCertificate *issuer = NULL;
+  gnutls_x509_crt_t cert;
+  int gerr;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), NULL);
+
+  if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+    return NULL;
+
+  /* Dig out the issuer of this certificate */
+  cert = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (certificate));
+  gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+  if (gerr < 0)
+    {
+      g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
+      return NULL;
+    }
+
+  subject = g_byte_array_new ();
+  g_byte_array_append (subject, dn.data, dn.size);
+  gnutls_free (dn.data);
+
+  /* Find the full DER value of the certificate */
+  g_mutex_lock (self->priv->mutex);
+  der = multi_byte_array_hash_lookup_one (self->priv->subjects, subject);
+  g_mutex_unlock (self->priv->mutex);
+
+  g_byte_array_unref (subject);
+
+  if (der != NULL)
+    {
+      datum.data = der->data;
+      datum.size = der->len;
+      issuer = g_tls_certificate_gnutls_new (&datum, NULL);
+    }
+
+  return issuer;
+}
+
+static GList*
+g_tls_file_database_gnutls_lookup_certificates_issued_by (GTlsDatabase           *database,
+                                                          GByteArray             *issuer_raw_dn,
+                                                          GTlsInteraction        *interaction,
+                                                          GTlsDatabaseLookupFlags flags,
+                                                          GCancellable           *cancellable,
+                                                          GError                **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  GByteArray *der;
+  gnutls_datum_t datum;
+  GList *issued = NULL;
+  GPtrArray *ders;
+  guint i;
+
+  /* We don't have any private keys here */
+  if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+    return NULL;
+
+  /* Find the full DER value of the certificate */
+  g_mutex_lock (self->priv->mutex);
+  ders = multi_byte_array_hash_lookup_all (self->priv->issuers, issuer_raw_dn);
+  g_mutex_unlock (self->priv->mutex);
+
+  for (i = 0; ders && i < ders->len; i++)
+    {
+      der = ders->pdata[i];
+      datum.data = der->data;
+      datum.size = der->len;
+      issued = g_list_prepend (issued, g_tls_certificate_gnutls_new (&datum, NULL));
+    }
+
+  return issued;
+}
+
+static void
+g_tls_file_database_gnutls_class_init (GTlsFileDatabaseGnutlsClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+  GTlsDatabaseGnutlsClass *gnutls_class = G_TLS_DATABASE_GNUTLS_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsFileDatabaseGnutlsPrivate));
+
+  gobject_class->get_property = g_tls_file_database_gnutls_get_property;
+  gobject_class->set_property = g_tls_file_database_gnutls_set_property;
+  gobject_class->finalize     = g_tls_file_database_gnutls_finalize;
+
+  database_class->create_certificate_handle = g_tls_file_database_gnutls_create_certificate_handle;
+  database_class->lookup_certificate_for_handle = g_tls_file_database_gnutls_lookup_certificate_for_handle;
+  database_class->lookup_certificate_issuer = g_tls_file_database_gnutls_lookup_certificate_issuer;
+  database_class->lookup_certificates_issued_by = g_tls_file_database_gnutls_lookup_certificates_issued_by;
+  gnutls_class->lookup_assertion = g_tls_file_database_gnutls_lookup_assertion;
+
+  g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
+}
+
+static void
+g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface)
+{
+
+}
+
+static gboolean
+g_tls_file_database_gnutls_initable_init (GInitable    *initable,
+                                          GCancellable *cancellable,
+                                          GError      **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (initable);
+  GHashTable *subjects, *issuers, *digests;
+  gboolean result;
+
+  subjects = multi_byte_array_hash_new ();
+  issuers = multi_byte_array_hash_new ();
+  digests = multi_byte_array_hash_new ();
+
+  result = load_anchor_file (self->priv->anchor_filename, subjects, issuers,
+                             digests, error);
+
+  if (result)
+    {
+      g_mutex_lock (self->priv->mutex);
+      if (!self->priv->subjects)
+        {
+          self->priv->subjects = subjects;
+          subjects = NULL;
+        }
+      if (!self->priv->issuers)
+        {
+          self->priv->issuers = issuers;
+          issuers = NULL;
+        }
+      if (!self->priv->digests)
+        {
+          self->priv->digests = digests;
+          digests = NULL;
+        }
+      g_mutex_unlock (self->priv->mutex);
+    }
+
+  if (subjects != NULL)
+    g_hash_table_unref (subjects);
+  if (issuers != NULL)
+    g_hash_table_unref (issuers);
+  if (digests != NULL)
+    g_hash_table_unref (digests);
+  return result;
+}
+
+static void
+g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface)
+{
+  iface->init = g_tls_file_database_gnutls_initable_init;
+}
diff --git a/tls/gnutls/gtlsfiledatabase-gnutls.h b/tls/gnutls/gtlsfiledatabase-gnutls.h
new file mode 100644
index 0000000..faa2c7d
--- /dev/null
+++ b/tls/gnutls/gtlsfiledatabase-gnutls.h
@@ -0,0 +1,52 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Collabora, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#ifndef __G_TLS_FILE_DATABASE_GNUTLS_H__
+#define __G_TLS_FILE_DATABASE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_FILE_DATABASE_GNUTLS            (g_tls_file_database_gnutls_get_type ())
+#define G_TLS_FILE_DATABASE_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutls))
+#define G_TLS_FILE_DATABASE_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutlsClass))
+#define G_IS_TLS_FILE_DATABASE_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS))
+#define G_IS_TLS_FILE_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_FILE_DATABASE_GNUTLS))
+#define G_TLS_FILE_DATABASE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutlsClass))
+
+typedef struct _GTlsFileDatabaseGnutlsPrivate                   GTlsFileDatabaseGnutlsPrivate;
+typedef struct _GTlsFileDatabaseGnutlsClass                     GTlsFileDatabaseGnutlsClass;
+typedef struct _GTlsFileDatabaseGnutls                          GTlsFileDatabaseGnutls;
+
+struct _GTlsFileDatabaseGnutlsClass
+{
+  GTlsDatabaseGnutlsClass parent_class;
+};
+
+struct _GTlsFileDatabaseGnutls
+{
+  GTlsDatabaseGnutls parent_instance;
+  GTlsFileDatabaseGnutlsPrivate *priv;
+};
+
+GType                        g_tls_file_database_gnutls_get_type              (void) G_GNUC_CONST;
+
+GTlsDatabase*                g_tls_file_database_gnutls_new                   (const gchar *anchor_file);
+
+G_END_DECLS
+
+#endif /* __G_TLS_FILE_DATABASE_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index e374b63..b9565ae 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -174,8 +174,32 @@ g_tls_server_connection_gnutls_verify_peer (GTlsConnectionGnutls  *gnutls,
 					    GTlsCertificate       *peer_certificate,
 					    GTlsCertificateFlags  *errors)
 {
+  GTlsDatabase *database;
+  GError *error = NULL;
+
+  database = g_tls_connection_get_database (G_TLS_CONNECTION (gnutls));
+  if (database == NULL)
+    {
+      *errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+      *errors |= g_tls_certificate_verify (peer_certificate, NULL, NULL);
+    }
+  else
+    {
+      *errors |= g_tls_database_verify_chain (database, peer_certificate,
+                                              G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT, NULL,
+                                              g_tls_connection_get_interaction (G_TLS_CONNECTION (gnutls)),
+                                              G_TLS_DATABASE_VERIFY_NONE,
+                                              NULL, &error);
+      if (error)
+        {
+          g_warning ("failure verifying certificate chain: %s",
+                     error->message);
+          g_clear_error (&error);
+        }
+    }
+
   return g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
-						   peer_certificate, *errors);
+                                                   peer_certificate, *errors);
 }
 
 static void
diff --git a/tls/tests/Makefile.am b/tls/tests/Makefile.am
new file mode 100644
index 0000000..1e9b316
--- /dev/null
+++ b/tls/tests/Makefile.am
@@ -0,0 +1,25 @@
+NULL =
+
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =              \
+	-g                  \
+	$(GLIB_CFLAGS)      \
+	-I$(top_srcdir)/tls \
+	-DSRCDIR=\""$(srcdir)"\"
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+LDADD  = \
+	$(GLIB_LIBS)
+
+TEST_PROGS =          \
+	tls           \
+	$(NULL)
+
+EXTRA_DIST = \
+	files \
+	$(NULL)
+
+DISTCLEANFILES = \
+	$(NULL)
diff --git a/tls/tests/files/ca-roots.pem b/tls/tests/files/ca-roots.pem
new file mode 100644
index 0000000..cf9e9d5
--- /dev/null
+++ b/tls/tests/files/ca-roots.pem
@@ -0,0 +1,202 @@
+-----BEGIN CERTIFICATE-----
+MIIDxjCCAy+gAwIBAgIJAOpd4Em2fjp3MA0GCSqGSIb3DQEBBQUAMIGGMRMwEQYK
+CZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPyLGQBGRYHRVhBTVBMRTEeMBwGA1UE
+CxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
+bTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNMDcxMjIwMTc1NjA2
+WhcNMzUwNTA4MTc1NjA2WjCBhjETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmS
+JomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0
+eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkqhkiG9w0BCQEWDmNhQGV4
+YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5OjHuXXN2LG3s
+FHISaZZ6L1RSYgRdTenu1nvqkMn/xvzOz385oede1z/7f6BoXyM0kNWCf4SOXtXr
+EIGmQoeURhFfLCnoK8NHfNcel3IPyMPhdJUMJlc3gfpWm+QxjkyqVyMhyYxC9Pmg
+QC7zx4ZKcQrL3zVGYtg8wxmaKY2HwQIDAQABo4IBODCCATQwHQYDVR0OBBYEFNSE
+nYhMCPaaFynFeQ2R5y25+AcFMIG7BgNVHSMEgbMwgbCAFNSEnYhMCPaaFynFeQ2R
+5y25+AcFoYGMpIGJMIGGMRMwEQYKCZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPy
+LGQBGRYHRVhBTVBMRTEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcw
+FQYDVQQDEw5jYS5leGFtcGxlLmNvbTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs
+ZS5jb22CCQDqXeBJtn46dzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTAZBgNVHRIEEjAQgQ5jYUBleGFt
+cGxlLmNvbTANBgkqhkiG9w0BAQUFAAOBgQA6xjU2aPgMOh2yyz2KCb6d5gNNvfr4
+pLGpZWilbRkA36OOG43zxeRZoumh1ybyOvhm73cMvNihDUyOf7vQe75Qtp5koGPS
+V3mSruhsRGvOZxcV+SJnBj1exKyH3mdaZA74Xg4y5qkUkywPqnP5Y+E6UMJM7Nmw
+kHk2bKJC5vjxoA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGCDCCA/CgAwIBAgIBATANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wNTEwMTQwNzM2NTVaFw0zMzAzMjgwNzM2NTVaMFQxFDAS
+BgNVBAoTC0NBY2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5v
+cmcxHDAaBgNVBAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCrSTURSHzSJn5TlM9Dqd0o10Iqi/OHeBlYfA+e2ol9
+4fvrcpANdKGWZKufoCSZc9riVXbHF3v1BKxGuMO+f2SNEGwk82GcwPKQ+lHm9WkB
+Y8MPVuJKQs/iRIwlKKjFeQl9RrmK8+nzNCkIReQcn8uUBByBqBSzmGXEQ+xOgo0J
+0b2qW42S0OzekMV/CsLj6+YxWl50PpczWejDAz1gM7/30W9HxM3uYoNSbi4ImqTZ
+FRiRpoWSR7CuSOtttyHshRpocjWr//AQXcD0lKdq1TuSfkyQBX6TwSyLpI5idBVx
+bgtxA+qvFTia1NIFcm+M+SvrWnIl+TlG43IbPgTDZCciECqKT1inA62+tC4T7V2q
+SNfVfdQqe1z6RgRQ5MwOQluM7dvyz/yWk+DbETZUYjQ4jwxgmzuXVjit89Jbi6Bb
+6k6WuHzX1aCGcEDTkSm3ojyt9Yy7zxqSiuQ0e8DYbF/pCsLDpyCaWt8sXVJcukfV
+m+8kKHA4IC/VfynAskEDaJLM4JzMl0tF7zoQCqtwOpiVcK01seqFK6QcgCExqa5g
+eoAmSAC4AcCTY1UikTxW56/bOiXzjzFU6iaLgVn5odFTEcV7nQP2dBHgbbEsPyyG
+kZlxmqZ3izRg0RS0LKydr4wQ05/EavhvE/xzWfdmQnQeiuP43NJvmJzLR5iVQAX7
+6QIDAQABo4G/MIG8MA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUHAQEEUTBPMCMG
+CCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggrBgEFBQcwAoYc
+aHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBBMD8GCCsGAQQB
+gZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9yZy9pbmRleC5w
+aHA/aWQ9MTAwDQYJKoZIhvcNAQEEBQADggIBAH8IiKHaGlBJ2on7oQhy84r3HsQ6
+tHlbIDCxRd7CXdNlafHCXVRUPIVfuXtCkcKZ/RtRm6tGpaEQU55tiKxzbiwzpvD0
+nuB1wT6IRanhZkP+VlrRekF490DaSjrxC1uluxYG5sLnk7mFTZdPsR44Q4Dvmw2M
+77inYACHV30eRBzLI++bPJmdr7UpHEV5FpZNJ23xHGzDwlVks7wU4vOkHx4y/CcV
+Bc/dLq4+gmF78CEQGPZE6lM5+dzQmiDgxrvgu1pPxJnIB721vaLbLmINQjRBvP+L
+ivVRIqqIMADisNS8vmW61QNXeZvo3MhN+FDtkaVSKKKs+zZYPumUK5FQhxvWXtaM
+zPcPEAxSTtAWYeXlCmy/F8dyRlecmPVsYGN6b165Ti/Iubm7aoW8mA3t+T6XhDSU
+rgCvoeXnkm5OvfPi2RSLXNLrAWygF6UtEOucekq9ve7O/e0iQKtwOIj1CodqwqsF
+YMlIBdpTwd5Ed2qz8zw87YC8pjhKKSRf/lk7myV6VmMAZLldpGJ9VzZPrYPvH5JT
+oI53V93lYRE9IwCQTDz6o2CTBKOvNfYOao9PSmCnhQVsRqGP9Md246FZV/dxssRu
+FFxtbUFm3xuTsdQAw+7Lzzw9IYCpX2Nl/N3gX6T0K/CFcUHUZyX7GrGXrtaZghNB
+0m6lG5kngOcLqagA
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl
+cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp
+Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow
+gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV
+BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm
+BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV
+BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ
+cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK
+P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ
+fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j
+kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB
+gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95
+B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl
+cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m
+cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz
+NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx
+EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp
+bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG
+SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N
+j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef
+QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY
+x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC
+neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr
+5PjRzneigQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl
+cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy
+ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5
+NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw
+EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n
+MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw
+IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3
+DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7
+7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j
+Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq
+W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx
+eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1
+KzGJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl
+IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1
+OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ
+BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg
+Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3
+DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI
+NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL
+lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN
+9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ
+a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU
+Qg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl
+IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG
+A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
+ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ
+VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz
+QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I
+/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC
+6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX
+TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD
+TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni
+TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/ca.pem b/tls/tests/files/ca.pem
new file mode 100644
index 0000000..b8d6008
--- /dev/null
+++ b/tls/tests/files/ca.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxjCCAy+gAwIBAgIJAOpd4Em2fjp3MA0GCSqGSIb3DQEBBQUAMIGGMRMwEQYK
+CZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPyLGQBGRYHRVhBTVBMRTEeMBwGA1UE
+CxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
+bTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNMDcxMjIwMTc1NjA2
+WhcNMzUwNTA4MTc1NjA2WjCBhjETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmS
+JomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0
+eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkqhkiG9w0BCQEWDmNhQGV4
+YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5OjHuXXN2LG3s
+FHISaZZ6L1RSYgRdTenu1nvqkMn/xvzOz385oede1z/7f6BoXyM0kNWCf4SOXtXr
+EIGmQoeURhFfLCnoK8NHfNcel3IPyMPhdJUMJlc3gfpWm+QxjkyqVyMhyYxC9Pmg
+QC7zx4ZKcQrL3zVGYtg8wxmaKY2HwQIDAQABo4IBODCCATQwHQYDVR0OBBYEFNSE
+nYhMCPaaFynFeQ2R5y25+AcFMIG7BgNVHSMEgbMwgbCAFNSEnYhMCPaaFynFeQ2R
+5y25+AcFoYGMpIGJMIGGMRMwEQYKCZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPy
+LGQBGRYHRVhBTVBMRTEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcw
+FQYDVQQDEw5jYS5leGFtcGxlLmNvbTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs
+ZS5jb22CCQDqXeBJtn46dzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTAZBgNVHRIEEjAQgQ5jYUBleGFt
+cGxlLmNvbTANBgkqhkiG9w0BAQUFAAOBgQA6xjU2aPgMOh2yyz2KCb6d5gNNvfr4
+pLGpZWilbRkA36OOG43zxeRZoumh1ybyOvhm73cMvNihDUyOf7vQe75Qtp5koGPS
+V3mSruhsRGvOZxcV+SJnBj1exKyH3mdaZA74Xg4y5qkUkywPqnP5Y+E6UMJM7Nmw
+kHk2bKJC5vjxoA==
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/client-and-key.pem b/tls/tests/files/client-and-key.pem
new file mode 100644
index 0000000..897b5f2
--- /dev/null
+++ b/tls/tests/files/client-and-key.pem
@@ -0,0 +1,45 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQkwDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0xMTAxMTgwNjA0MTFaFw0yMTAxMTUwNjA0
+MTFaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQA3LuElj2QB9wQvmIxk2Jmb
+IPP2/WS8dwPoCv/N3+6nTx8yRsrILf4QsnEbbsxoYO5jW4r9Kt8m8B/M7YgnBDE9
+zlm7JbXKZf2isSm5TyT627Ymzxrzs5d+7o2eS7SN1DB6PyvRh2ye7EMbyEYD8ULi
+itDUkYkssNCVivYwVvJoMg==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAwSA0Mz92De30Mc0A/P9vzstLERoqGhnwBw0HKbcsQ50KdcYS
+cp/Rv2WRPlxpe7kYAzzhqFMInufv1FU2uoYozuNsF1Jf5lR+SolA+E5cPb7SeJhC
+Jwf3afPvyTGaOVuLO93d8zUGU74L/741Z/YQCE5FUCO8msc2iQmnc9M1EnVZa14d
+5T0/B8aZYcpVzUiC9EUwfTzrTghnNfkJzEiD9vnqDbsDsIE+H8o7+opMMsUU0ZzV
+PZqy9j8/f4943rL6V4UdK0JO7tEGL+XiFzNl2fCrcEZPqaeMQ3vBq7azukDTrtJe
+KY06RiLl+DCykweLx8laZjIHKlSZAFHPB+bvsQIDAQABAoIBAQCQUI1RYnHIdPFO
+qZ+8bvDQ+g8tR30ApjM8QZsBrDRyjg579bhhWVY2jSJdFFdqseTkvoDt9KZzgGQy
+Kj9MYOZru3xRbSfmiWsaLbiUFJJPPaIvpa+BVS2oSjX8BYn2pJbF9MRfclc5CsIS
+qMNl3XUbj8mx2hKdIpJ5EvLD1adKE4Se6peqSZAmEHONNCsrMrQ0GSQqV3viInJr
+tc3kp3HcPffSROWqmc6jAJ77Cs3ApgJavL5RGjx30Kd+dKVq4PXZ+IhWM8dOSput
+wcyxEosiP/W2g0rDgNW2mGOVOwa/D5SnOolicHifdV7idjwLAjkyYgvmBMNSsECj
+yKBkE0gxAoGBAN8iHMumyvriHuj9bSLZ1bcyYFz7jIwUxpHTT7VqN/j/Y1BoBIBy
+ZZLDGMa+ID/brpRHzJQAKSNtbFQ0S1HTSKcFud5OWE8Rp3pQJU+sdeO3pCMWAD1z
+Q4ggF07JjTSSnK+4fcXgEN9P2OdfXy7Rj3HFpSahql55Kp5udoUdzUVFAoGBAN2S
+krlcEuqsEYjqsCJw5pctIwPMvCM51JgirrdETwSGquMklSrobH0PHMlR67gsA/9I
+UGShT0LL4UWYpBn/4xLrLbua5aHIBfQQZp9K6jDZddWS+EFL5JkO/Up4/qM6fUbH
+CuweVv1gd6i2Ti35K60mgx6MqVunaB1k8Q9P3Pl9AoGALSVtxha9Qv21W1bLWh3R
+C/v5W1baHQ2nD6I9omsXYB3sLjydjI+Y1ZT70lptk/4S2JWeYuOVb0GYhYD/LFMf
+hAu4i642V+kuhaTpp7ExOR3S6/ZrngNQSp6TmLFXDKgNY9BkQkEPqN8y971oOMTV
+zSM8QxC6s9q4MM4Q1OYuvjECgYEAsO2V1AW95T45Ukd1FktpFlaomyQlJ0vKgyFO
+unEFV+vhETfpFTY7SzGCHxAXVh1vo62u5Gwayo/a9qQIhepa/IRnJGNv8luyxU1D
+ZPeBQjija0PMkPd1NvNNNuafDuBpoNbX1ev0MqeRZVsN2pAZXE5gbUiNA+8NqEsu
+Yre3EFECgYEA13rXE76zZgsefx+2spjqJDUWEmTDd1460xTtxCCgL9dy4rW5bgwo
+MvINphSUXOwSkn8Oja/IvpN28zSj9W/ci5wU52P5w4blkBmuj8UoCjP2FN1b1OBa
+86mkwVsCYUyyI2apuwrHP77yeb8jXZb+reqSns3hU+HyO/nUTVmnews=
+-----END RSA PRIVATE KEY-----
diff --git a/tls/tests/files/client-future.pem b/tls/tests/files/client-future.pem
new file mode 100644
index 0000000..de1cb75
--- /dev/null
+++ b/tls/tests/files/client-future.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQowDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0yMDAxMTgxNzI3MDNaFw0yMTAxMTcxNzI3
+MDNaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBvt8v930fQtxR7f7Vcb1Hg
+irq1CtffsBqtKYupYg6IgloiRA6U5wdU0e6faA3Ppsmd4SmNKb9ZavIgnDBfx8MP
+1/IpsNOkg0366bP/zzkAhcXspo7PU8yZIqep//wT4TOFz04N8Lshqm8HUejShFdA
+fB8C0LX5Y/2219ZVMaaEbw==
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/client-past.pem b/tls/tests/files/client-past.pem
new file mode 100644
index 0000000..2dbb4d1
--- /dev/null
+++ b/tls/tests/files/client-past.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQswDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0wMDAxMTgxNzI3NDdaFw0wMTAxMTcxNzI3
+NDdaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBC3BOULAOkRFLKLajHIIB2
+VB0tHOFWuflP/LXso3ogGA8ItqbjacqjRHdTGK79etbxSTdi7k8owMVMPavJnBYk
+TraOkf/xxHo2zWy3XES1lniTUfGgKpjYNlALB6K6DJseZorSOmGA4KllL46MYwNu
+jsLO+5HkS/uNxlKo2l+xGw==
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/client.pem b/tls/tests/files/client.pem
new file mode 100644
index 0000000..04bc8ac
--- /dev/null
+++ b/tls/tests/files/client.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQkwDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0xMTAxMTgwNjA0MTFaFw0yMTAxMTUwNjA0
+MTFaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQA3LuElj2QB9wQvmIxk2Jmb
+IPP2/WS8dwPoCv/N3+6nTx8yRsrILf4QsnEbbsxoYO5jW4r9Kt8m8B/M7YgnBDE9
+zlm7JbXKZf2isSm5TyT627Ymzxrzs5d+7o2eS7SN1DB6PyvRh2ye7EMbyEYD8ULi
+itDUkYkssNCVivYwVvJoMg==
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/server-and-key.pem b/tls/tests/files/server-and-key.pem
new file mode 100644
index 0000000..7bf1cff
--- /dev/null
+++ b/tls/tests/files/server-and-key.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk
+ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp
+ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq
+hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx
+NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW
+B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3
+DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf
+hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC
+MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1
+Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk
+PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH
+QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOgIBAAJBANhJxPGTnlcGZsMz3PPA76CtXJITiu51a7MfY+k5siiept+FGgBq
+UpWW4b2O9scjzV5fEJ1I5u/UoE/Wxs3lHOkCAwEAAQJAURSWd9lW6ljD/TlcAyS4
+sAtNQJWC55GtJiEGW0/9savXVHPRllN5IlcOJS/L//rD47UzwmGHcxxzsg3p+s51
+YQIhAPaiU+wbUL0cQrxqwIa/TEtNtrU3T21vNvQaFLSHh+uHAiEA4IBuYuPL+xz2
+nDwSB9UWklYV5fKhwqUA96qyMNxTMA8CIGvD6h+Un+bB3nctvgoitFeDEX6FOHN8
+0OpAKyPmxIEpAiEAiwNb1wYhN9QebG3R6GGtQV3m32VXftR3feSHxDOZm0cCIEpA
+7kMFeeQQZb1qYd1PMFSDCG+BkrWh/Fud+VvFAWxI
+-----END RSA PRIVATE KEY-----
diff --git a/tls/tests/files/server-self.pem b/tls/tests/files/server-self.pem
new file mode 100644
index 0000000..20b3500
--- /dev/null
+++ b/tls/tests/files/server-self.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBiDCCATICCQDJ4QeFpYPYljANBgkqhkiG9w0BAQUFADBLMRMwEQYKCZImiZPy
+LGQBGRYDQ09NMRcwFQYKCZImiZPyLGQBGRYHRVhBTVBMRTEbMBkGA1UEAxMSc2Vy
+dmVyLmV4YW1wbGUuY29tMB4XDTExMDExOTAzMTYzOFoXDTIxMDExNjAzMTYzOFow
+SzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUx
+GzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA
+MEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbfhRoAalKVluG9
+jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAEwDQYJKoZIhvcNAQEFBQADQQAagc2P
+/lCfDwT3max+D2M7++KMDfGqiO3gI+hMarf/jAaQpcKO/9G95AnNo4lTd6W6/7yj
+YYvUupv+0vi4CtQG
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/server.der b/tls/tests/files/server.der
new file mode 100644
index 0000000..cf2de65
Binary files /dev/null and b/tls/tests/files/server.der differ
diff --git a/tls/tests/files/server.pem b/tls/tests/files/server.pem
new file mode 100644
index 0000000..d4bd526
--- /dev/null
+++ b/tls/tests/files/server.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk
+ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp
+ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq
+hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx
+NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW
+B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3
+DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf
+hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC
+MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1
+Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk
+PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH
+QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4=
+-----END CERTIFICATE-----
diff --git a/tls/tests/tls.c b/tls/tests/tls.c
new file mode 100644
index 0000000..60dd033
--- /dev/null
+++ b/tls/tests/tls.c
@@ -0,0 +1,887 @@
+/* GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+/* -----------------------------------------------------------------------------
+ * CONNECTION AND DATABASE TESTS
+ */
+
+#define TEST_DATA "You win again, gravity!\n"
+#define TEST_DATA_LENGTH 24
+
+typedef struct {
+  GMainLoop *loop;
+  GSocketService *service;
+  GTlsDatabase *database;
+  GIOStream *server_connection;
+  GIOStream *client_connection;
+  GSocketConnectable *identity;
+  GSocketAddress *address;
+  GTlsAuthenticationMode auth_mode;
+  GTlsCertificateFlags accept_flags;
+} TestConnection;
+
+static void
+setup_connection (TestConnection *test, gconstpointer data)
+{
+  GInetAddress *inet;
+  guint16 port;
+
+  test->loop = g_main_loop_new (NULL, FALSE);
+
+  test->auth_mode = G_TLS_AUTHENTICATION_NONE;
+
+  /* This is where the server listens and the client connects */
+  port = g_random_int_range (50000, 65000);
+  inet = g_inet_address_new_from_string ("127.0.0.1");
+  test->address = G_SOCKET_ADDRESS (g_inet_socket_address_new (inet, port));
+  g_object_unref (inet);
+
+  /* The identity matches the server certificate */
+  test->identity = g_network_address_new ("server.example.com", port);
+}
+
+static void
+teardown_connection (TestConnection *test, gconstpointer data)
+{
+  if (test->service)
+    g_object_unref (test->service);
+
+  if (test->server_connection)
+    {
+      g_assert (G_IS_TLS_SERVER_CONNECTION (test->server_connection));
+      g_object_unref (test->server_connection);
+      g_assert (!G_IS_TLS_SERVER_CONNECTION (test->server_connection));
+    }
+
+  if (test->client_connection)
+    {
+      g_assert (G_IS_TLS_CLIENT_CONNECTION (test->client_connection));
+      g_object_unref (test->client_connection);
+      g_assert (!G_IS_TLS_SERVER_CONNECTION (test->client_connection));
+    }
+
+  if (test->database)
+    {
+      g_assert (G_IS_TLS_DATABASE (test->database));
+      g_object_unref (test->database);
+      g_assert (!G_IS_TLS_DATABASE (test->database));
+    }
+
+  g_object_unref (test->address);
+  g_object_unref (test->identity);
+  g_main_loop_unref (test->loop);
+}
+
+static gboolean
+on_accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert,
+                       GTlsCertificateFlags errors, gpointer user_data)
+{
+  TestConnection *test = user_data;
+  return errors == test->accept_flags;
+}
+
+static void
+on_output_close_finish (GObject        *object,
+                        GAsyncResult   *res,
+                        gpointer        user_data)
+{
+  GError *error = NULL;
+  g_output_stream_close_finish (G_OUTPUT_STREAM (object), res, &error);
+  g_assert_no_error (error);
+}
+
+static void
+on_output_write_finish (GObject        *object,
+                        GAsyncResult   *res,
+                        gpointer        user_data)
+{
+  TestConnection *test = user_data;
+  GError *error = NULL;
+  g_output_stream_write_finish (G_OUTPUT_STREAM (object), res, &error);
+  g_assert_no_error (error);
+
+  g_output_stream_close_async (G_OUTPUT_STREAM (object), G_PRIORITY_DEFAULT, NULL,
+                               on_output_close_finish, test);
+}
+
+static gboolean
+on_incoming_connection (GSocketService     *service,
+                        GSocketConnection  *connection,
+                        GObject            *source_object,
+                        gpointer            user_data)
+{
+  TestConnection *test = user_data;
+  GOutputStream *stream;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "server-and-key.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_free (path);
+
+  test->server_connection = g_tls_server_connection_new (G_IO_STREAM (connection),
+                                                         cert, &error);
+  g_assert_no_error (error);
+  g_object_unref (cert);
+
+  g_object_set (test->server_connection, "authentication-mode", test->auth_mode, NULL);
+  g_signal_connect (test->server_connection, "accept-certificate",
+                    G_CALLBACK (on_accept_certificate), test);
+
+  if (test->database)
+    g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
+
+  stream = g_io_stream_get_output_stream (test->server_connection);
+
+  g_output_stream_write_async (stream, TEST_DATA, TEST_DATA_LENGTH,
+                               G_PRIORITY_DEFAULT, NULL,
+                               on_output_write_finish, test);
+  return FALSE;
+}
+
+static void
+start_server_service (TestConnection *test, GTlsAuthenticationMode auth_mode)
+{
+  GError *error = NULL;
+
+  test->service = g_socket_service_new ();
+  g_socket_listener_add_address (G_SOCKET_LISTENER (test->service),
+                                 G_SOCKET_ADDRESS (test->address),
+                                 G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP,
+                                 NULL, NULL, &error);
+  g_assert_no_error (error);
+
+  test->auth_mode = auth_mode;
+  g_signal_connect (test->service, "incoming", G_CALLBACK (on_incoming_connection), test);
+}
+
+static GIOStream*
+start_server_and_connect_to_it (TestConnection *test, GTlsAuthenticationMode auth_mode)
+{
+  GSocketClient *client;
+  GError *error = NULL;
+  GSocketConnection *connection;
+
+  start_server_service (test, auth_mode);
+
+  client = g_socket_client_new ();
+  connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                        NULL, &error);
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  return G_IO_STREAM (connection);
+}
+
+static void
+on_input_read_finish (GObject        *object,
+                      GAsyncResult   *res,
+                      gpointer        user_data)
+{
+  TestConnection *test = user_data;
+  GError *error = NULL;
+  gchar *line, *check;
+
+  line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (object), res,
+                                               NULL, &error);
+  g_assert_no_error (error);
+  g_assert (line);
+
+  check = g_strdup (TEST_DATA);
+  g_strstrip (check);
+  g_assert_cmpstr (line, ==, check);
+  g_free (check);
+  g_free (line);
+
+  g_main_loop_quit (test->loop);
+}
+
+static void
+read_test_data_async (TestConnection *test)
+{
+  GDataInputStream *stream;
+
+  stream = g_data_input_stream_new (g_io_stream_get_input_stream (test->client_connection));
+  g_assert (stream);
+
+  g_data_input_stream_read_line_async (stream, G_PRIORITY_DEFAULT, NULL,
+                                       on_input_read_finish, test);
+  g_object_unref (stream);
+}
+
+static void
+test_basic_connection (TestConnection *test,
+                       gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  /* No validation at all in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+}
+
+static void
+test_verified_connection (TestConnection *test,
+                          gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "ca-roots.pem", NULL);
+  test->database = g_tls_file_database_new (path, &error);
+  g_assert_no_error (error);
+  g_assert (test->database);
+  g_free (path);
+
+  connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert (test->client_connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+}
+
+static void
+test_client_auth_connection (TestConnection *test,
+                             gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  GTlsCertificate *cert;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "ca-roots.pem", NULL);
+  test->database = g_tls_file_database_new (path, &error);
+  g_assert_no_error (error);
+  g_assert (test->database);
+  g_free (path);
+
+  connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert (test->client_connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  path = g_build_filename (SRCDIR, "files", "client-and-key.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_free (path);
+
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+  g_object_unref (cert);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+}
+
+static void
+test_connection_no_database (TestConnection *test,
+                             gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert (test->client_connection);
+
+  /* Overrides loading of the default database */
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), NULL);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  test->accept_flags = G_TLS_CERTIFICATE_UNKNOWN_CA;
+  g_signal_connect (test->client_connection, "accept-certificate",
+                    G_CALLBACK (on_accept_certificate), test);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+}
+
+/* -----------------------------------------------------------------------------
+ * CERTIFICATE TESTS
+ */
+
+typedef struct {
+  gchar *pem;
+  gsize pem_length;
+  GByteArray *der;
+} TestCertificate;
+
+static void
+setup_certificate (TestCertificate *test, gconstpointer data)
+{
+  GError *error = NULL;
+  gchar *path;
+  gchar *contents;
+  gsize length;
+
+  path = g_build_filename (SRCDIR, "files", "server.pem", NULL);
+  g_file_get_contents (path, &test->pem, &test->pem_length, &error);
+  g_assert_no_error (error);
+  g_free (path);
+
+  path = g_build_filename (SRCDIR, "files", "server.der", NULL);
+  g_file_get_contents (path, &contents, &length, &error);
+  g_assert_no_error (error);
+  g_free (path);
+
+  test->der = g_byte_array_new ();
+  g_byte_array_append (test->der, (guint8*)contents, length);
+  g_free (contents);
+}
+
+static void
+teardown_certificate (TestCertificate *test, gconstpointer data)
+{
+  g_free (test->pem);
+  g_byte_array_free (test->der, TRUE);
+}
+
+static void
+test_create_destroy_certificate_pem (TestCertificate *test, gconstpointer data)
+{
+  GTlsCertificate *cert;
+  gchar *pem = NULL;
+  GError *error = NULL;
+
+  cert = g_tls_certificate_new_from_pem (test->pem, test->pem_length, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+
+  g_object_get (cert, "certificate-pem", &pem, NULL);
+  g_assert_cmpstr (pem, ==, test->pem);
+  g_free (pem);
+
+  g_object_unref (cert);
+  g_assert (!G_IS_TLS_CERTIFICATE (cert));
+}
+
+static void
+test_create_destroy_certificate_der (TestCertificate *test, gconstpointer data)
+{
+  GTlsCertificate *cert;
+  GByteArray *der = NULL;
+  GError *error = NULL;
+  GTlsBackend *backend;
+
+  backend = g_tls_backend_get_default ();
+  cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                         NULL, &error,
+                         "certificate", test->der,
+                         NULL);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+
+  g_object_get (cert, "certificate", &der, NULL);
+  g_assert (der);
+  g_assert_cmpuint (der->len, ==, test->der->len);
+  g_assert (memcmp (der->data, test->der->data, der->len) == 0);
+  g_byte_array_unref (der);
+
+  g_object_unref (cert);
+  g_assert (!G_IS_TLS_CERTIFICATE (cert));
+}
+
+static void
+test_create_certificate_with_issuer (TestCertificate   *test,
+                                     gconstpointer      data)
+{
+  GTlsCertificate *cert, *issuer, *check;
+  GError *error = NULL;
+  GTlsBackend *backend;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "ca.pem", NULL);
+  issuer = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (issuer));
+  g_free (path);
+
+  backend = g_tls_backend_get_default ();
+  cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                         NULL, &error,
+                         "certificate-pem", test->pem,
+                         "issuer", issuer,
+                         NULL);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+
+  g_object_unref (issuer);
+  g_assert (G_IS_TLS_CERTIFICATE (issuer));
+
+  check = g_tls_certificate_get_issuer (cert);
+  g_assert (check == issuer);
+
+  g_object_unref (cert);
+  g_assert (!G_IS_TLS_CERTIFICATE (cert));
+  g_assert (!G_IS_TLS_CERTIFICATE (issuer));
+}
+
+/* -----------------------------------------------------------------------------
+ * CERTIFICATE VERIFY
+ */
+
+typedef struct {
+  GTlsCertificate *cert;
+  GTlsCertificate *anchor;
+  GSocketConnectable *identity;
+  GTlsDatabase *database;
+} TestVerify;
+
+static void
+setup_verify (TestVerify     *test,
+              gconstpointer   data)
+{
+  GError *error = NULL;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "server.pem", NULL);
+  test->cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (test->cert));
+  g_free (path);
+
+  test->identity = g_network_address_new ("server.example.com", 80);
+
+  path = g_build_filename (SRCDIR, "files", "ca.pem", NULL);
+  test->anchor = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (test->anchor));
+  test->database = g_tls_file_database_new (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_DATABASE (test->database));
+  g_free (path);
+}
+
+static void
+teardown_verify (TestVerify      *test,
+                 gconstpointer    data)
+{
+  g_assert (G_IS_TLS_CERTIFICATE (test->cert));
+  g_object_unref (test->cert);
+  g_assert (!G_IS_TLS_CERTIFICATE (test->cert));
+
+  g_assert (G_IS_TLS_CERTIFICATE (test->anchor));
+  g_object_unref (test->anchor);
+  g_assert (!G_IS_TLS_CERTIFICATE (test->anchor));
+
+  g_assert (G_IS_TLS_DATABASE (test->database));
+  g_object_unref (test->database);
+  g_assert (!G_IS_TLS_DATABASE (test->database));
+}
+
+static void
+test_verify_certificate_good (TestVerify      *test,
+                              gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+
+  errors = g_tls_certificate_verify (test->cert, test->identity, test->anchor);
+  g_assert_cmpuint (errors, ==, 0);
+
+  errors = g_tls_certificate_verify (test->cert, NULL, test->anchor);
+  g_assert_cmpuint (errors, ==, 0);
+}
+
+static void
+test_verify_certificate_bad_identity (TestVerify      *test,
+                                      gconstpointer    data)
+{
+  GSocketConnectable *identity;
+  GTlsCertificateFlags errors;
+
+  identity = g_network_address_new ("other.example.com", 80);
+
+  errors = g_tls_certificate_verify (test->cert, identity, test->anchor);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+  g_object_unref (identity);
+}
+
+static void
+test_verify_certificate_bad_ca (TestVerify      *test,
+                                gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  /* Use a client certificate as the CA, which is wrong */
+  path = g_build_filename (SRCDIR, "files", "client.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  errors = g_tls_certificate_verify (test->cert, test->identity, cert);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+
+  g_object_unref (cert);
+}
+
+static void
+test_verify_certificate_bad_before (TestVerify      *test,
+                                    gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  /* This is a certificate in the future */
+  path = g_build_filename (SRCDIR, "files", "client-future.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  errors = g_tls_certificate_verify (cert, NULL, test->anchor);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_NOT_ACTIVATED);
+
+  g_object_unref (cert);
+}
+
+static void
+test_verify_certificate_bad_expired (TestVerify      *test,
+                                     gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  /* This is a certificate in the future */
+  path = g_build_filename (SRCDIR, "files", "client-past.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  errors = g_tls_certificate_verify (cert, NULL, test->anchor);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_EXPIRED);
+
+  g_object_unref (cert);
+}
+
+static void
+test_verify_certificate_bad_combo (TestVerify      *test,
+                                   gconstpointer    data)
+{
+  GTlsCertificate *cert;
+  GTlsCertificate *cacert;
+  GSocketConnectable *identity;
+  GTlsCertificateFlags errors;
+  GError *error = NULL;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "client-past.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  /* Unrelated cert used as certificate authority */
+  path = g_build_filename (SRCDIR, "files", "server-self.pem", NULL);
+  cacert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cacert));
+  g_free (path);
+
+  /*
+   * - Use unrelated cert as CA
+   * - Use wrong identity.
+   * - Use expired certificate.
+   */
+
+  identity = g_network_address_new ("other.example.com", 80);
+
+  errors = g_tls_certificate_verify (cert, identity, cacert);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA |
+                    G_TLS_CERTIFICATE_BAD_IDENTITY | G_TLS_CERTIFICATE_EXPIRED);
+
+  g_object_unref (cert);
+  g_object_unref (cacert);
+  g_object_unref (identity);
+}
+
+static void
+test_verify_database_good (TestVerify      *test,
+                           gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GError *error = NULL;
+
+  errors = g_tls_database_verify_chain (test->database, test->cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        test->identity, NULL, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpuint (errors, ==, 0);
+
+  errors = g_tls_database_verify_chain (test->database, test->cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        NULL, NULL, 0, NULL, &error);
+  g_assert_cmpuint (errors, ==, 0);
+}
+
+static void
+test_verify_database_bad_identity (TestVerify      *test,
+                                   gconstpointer    data)
+{
+  GSocketConnectable *identity;
+  GTlsCertificateFlags errors;
+  GError *error = NULL;
+
+  identity = g_network_address_new ("other.example.com", 80);
+
+  errors = g_tls_database_verify_chain (test->database, test->cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        identity, NULL, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+  g_object_unref (identity);
+}
+
+static void
+test_verify_database_bad_ca (TestVerify      *test,
+                             gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  /* Use another certificate which isn't in our CA list */
+  path = g_build_filename (SRCDIR, "files", "server-self.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  errors = g_tls_database_verify_chain (test->database, cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        test->identity, NULL, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+
+  g_object_unref (cert);
+}
+
+static void
+test_verify_database_bad_before (TestVerify      *test,
+                                 gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  /* This is a certificate in the future */
+  path = g_build_filename (SRCDIR, "files", "client-future.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  errors = g_tls_database_verify_chain (test->database, cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        NULL, NULL, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_NOT_ACTIVATED);
+
+  g_object_unref (cert);
+}
+
+static void
+test_verify_database_bad_expired (TestVerify      *test,
+                                  gconstpointer    data)
+{
+  GTlsCertificateFlags errors;
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *path;
+
+  /* This is a certificate in the future */
+  path = g_build_filename (SRCDIR, "files", "client-past.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  errors = g_tls_database_verify_chain (test->database, cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        NULL, NULL, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_EXPIRED);
+
+  g_object_unref (cert);
+}
+
+static void
+test_verify_database_bad_combo (TestVerify      *test,
+                                gconstpointer    data)
+{
+  GTlsCertificate *cert;
+  GSocketConnectable *identity;
+  GTlsCertificateFlags errors;
+  GError *error = NULL;
+  gchar *path;
+
+  path = g_build_filename (SRCDIR, "files", "server-self.pem", NULL);
+  cert = g_tls_certificate_new_from_file (path, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_TLS_CERTIFICATE (cert));
+  g_free (path);
+
+  /*
+   * - Use is self signed
+   * - Use wrong identity.
+   */
+
+  identity = g_network_address_new ("other.example.com", 80);
+
+  errors = g_tls_database_verify_chain (test->database, cert,
+                                        G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                        identity, NULL, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA |
+                    G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+  g_object_unref (cert);
+  g_object_unref (identity);
+}
+
+/* -----------------------------------------------------------------------------
+ * BACKEND
+ */
+
+static void
+test_default_database_is_singleton (void)
+{
+  GTlsBackend *backend;
+  GTlsDatabase *database;
+  GTlsDatabase *check;
+
+  backend = g_tls_backend_get_default ();
+  g_assert (G_IS_TLS_BACKEND (backend));
+
+  database = g_tls_backend_get_default_database (backend);
+  g_assert (G_IS_TLS_DATABASE (database));
+
+  check = g_tls_backend_get_default_database (backend);
+  g_assert (database == check);
+
+  g_object_unref (database);
+  g_object_unref (check);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  /* Use the gnutls database */
+  if (!g_getenv ("GIO_USE_TLS"))
+      g_setenv ("GIO_USE_TLS", "gnutls", TRUE);
+
+  g_test_add ("/tls/connection/basic", TestConnection, NULL,
+              setup_connection, test_basic_connection, teardown_connection);
+  g_test_add ("/tls/connection/verified", TestConnection, NULL,
+              setup_connection, test_verified_connection, teardown_connection);
+  g_test_add ("/tls/connection/client-auth", TestConnection, NULL,
+              setup_connection, test_client_auth_connection, teardown_connection);
+  g_test_add ("/tls/connection/no-database", TestConnection, NULL,
+              setup_connection, test_connection_no_database, teardown_connection);
+
+  g_test_add_func ("/tls/backend/default-database-is-singleton",
+                   test_default_database_is_singleton);
+
+  g_test_add ("/tls/certificate/create-destroy-pem", TestCertificate, NULL,
+              setup_certificate, test_create_destroy_certificate_pem, teardown_certificate);
+  g_test_add ("/tls/certificate/create-destroy-der", TestCertificate, NULL,
+              setup_certificate, test_create_destroy_certificate_der, teardown_certificate);
+  g_test_add ("/tls/certificate/create-with-issuer", TestCertificate, NULL,
+              setup_certificate, test_create_certificate_with_issuer, teardown_certificate);
+
+  g_test_add ("/tls/certificate/verify-good", TestVerify, NULL,
+              setup_verify, test_verify_certificate_good, teardown_verify);
+  g_test_add ("/tls/certificate/verify-bad-identity", TestVerify, NULL,
+              setup_verify, test_verify_certificate_bad_identity, teardown_verify);
+  g_test_add ("/tls/certificate/verify-bad-ca", TestVerify, NULL,
+              setup_verify, test_verify_certificate_bad_ca, teardown_verify);
+  g_test_add ("/tls/certificate/verify-bad-before", TestVerify, NULL,
+              setup_verify, test_verify_certificate_bad_before, teardown_verify);
+  g_test_add ("/tls/certificate/verify-bad-expired", TestVerify, NULL,
+              setup_verify, test_verify_certificate_bad_expired, teardown_verify);
+  g_test_add ("/tls/certificate/verify-bad-combo", TestVerify, NULL,
+              setup_verify, test_verify_certificate_bad_combo, teardown_verify);
+  g_test_add ("/tls/database/verify-good", TestVerify, NULL,
+              setup_verify, test_verify_database_good, teardown_verify);
+  g_test_add ("/tls/database/verify-bad-identity", TestVerify, NULL,
+              setup_verify, test_verify_database_bad_identity, teardown_verify);
+  g_test_add ("/tls/database/verify-bad-ca", TestVerify, NULL,
+              setup_verify, test_verify_database_bad_ca, teardown_verify);
+  g_test_add ("/tls/database/verify-bad-before", TestVerify, NULL,
+              setup_verify, test_verify_database_bad_before, teardown_verify);
+  g_test_add ("/tls/database/verify-bad-expired", TestVerify, NULL,
+              setup_verify, test_verify_database_bad_expired, teardown_verify);
+  g_test_add ("/tls/database/verify-bad-combo", TestVerify, NULL,
+              setup_verify, test_verify_database_bad_combo, teardown_verify);
+
+  return g_test_run();
+}
