[geary/cherry-pick-423a55b0] Application.TlsDatabase: Add unit tests for local (non-GCR) pinning
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/cherry-pick-423a55b0] Application.TlsDatabase: Add unit tests for local (non-GCR) pinning
- Date: Tue, 25 Aug 2020 07:04:43 +0000 (UTC)
commit 50d5d4aa74ac0ea48bd12f2dae064b327f066458
Author: Michael Gratton <mike vee net>
Date: Tue Aug 25 16:37:08 2020 +1000
Application.TlsDatabase: Add unit tests for local (non-GCR) pinning
Make the class internal so it can be tested, add unit tests covering
both in-memory-only and on-disk pinning.
.../application-certificate-manager.vala | 9 +-
.../application-certificate-manager-test.vala | 216 +++++++++++++++++++++
test/meson.build | 1 +
test/test-client.vala | 1 +
4 files changed, 225 insertions(+), 2 deletions(-)
---
diff --git a/src/client/application/application-certificate-manager.vala
b/src/client/application/application-certificate-manager.vala
index 65f6af4fa..ff0e7785a 100644
--- a/src/client/application/application-certificate-manager.vala
+++ b/src/client/application/application-certificate-manager.vala
@@ -153,8 +153,13 @@ public class Application.CertificateManager : GLib.Object {
}
-/** TLS database that observes locally pinned certs. */
-private class Application.TlsDatabase : GLib.TlsDatabase {
+/**
+ * TLS database that observes locally pinned certs.
+ *
+ * An instance of this is managed by {@link CertificateManager}, the
+ * application should simply construct an instance of that.
+ */
+internal class Application.TlsDatabase : GLib.TlsDatabase {
/** A certificate and the identities it is trusted for. */
diff --git a/test/client/application/application-certificate-manager-test.vala
b/test/client/application/application-certificate-manager-test.vala
new file mode 100644
index 000000000..6cb74b04d
--- /dev/null
+++ b/test/client/application/application-certificate-manager-test.vala
@@ -0,0 +1,216 @@
+/*
+ * Copyright © 2020 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+
+class Application.CertificateManagerTest : TestCase {
+
+
+ private const string IDENITY_HOSTNAME = "localhost";
+ private const uint16 IDENITY_PORT = 143;
+
+ private static int cert_id = 1;
+
+ private GLib.File? tmp = null;
+ private GLib.File? db_dir = null;
+ private GLib.File? cert_dir = null;
+
+
+ public CertificateManagerTest() {
+ base("Application.CertificateManagerTest");
+ add_test(
+ "database_memory_certificate_pinning_without_gcr",
+ database_memory_certificate_pinning_without_gcr
+ );
+ add_test(
+ "database_disk_certificate_pinning_without_gcr",
+ database_disk_certificate_pinning_without_gcr
+ );
+ }
+
+ public override void set_up() throws GLib.Error {
+ this.tmp = GLib.File.new_for_path(
+ GLib.DirUtils.make_tmp("application-certificate-manager-test-XXXXXX")
+ );
+ this.db_dir = this.tmp.get_child("db");
+ this.db_dir.make_directory();
+ this.cert_dir = this.tmp.get_child("certs");
+ this.cert_dir.make_directory();
+ }
+
+ public override void tear_down() throws GLib.Error {
+ delete_file(this.tmp);
+ this.db_dir = null;
+ this.cert_dir = null;
+ this.tmp = null;
+ }
+
+ public void database_memory_certificate_pinning_without_gcr()
+ throws GLib.Error {
+ var test_article1 = new Application.TlsDatabase(
+ GLib.TlsBackend.get_default().get_default_database(),
+ this.db_dir,
+ false
+ );
+ var id = new_identity();
+ var cert1 = new_cert("cert1");
+ var cert2 = new_cert("cert2");
+
+ // Assert the db doesn't know about the cert first up.
+ assert_pinning(test_article1, cert1, id, false);
+ assert_pinning(test_article1, cert2, id, false);
+
+ // Pin a cert in the db
+ test_article1.pin_certificate.begin(
+ cert1, id, false, null, this.async_completion
+ );
+ test_article1.pin_certificate.end(this.async_result());
+
+ // Assert the db now knows about it, but not the other
+ assert_pinning(test_article1, cert1, id, true);
+ assert_pinning(test_article1, cert2, id, false);
+
+ // Construct a new test article and ensure it doesn't know
+ // about either
+ var test_article2 = new Application.TlsDatabase(
+ GLib.TlsBackend.get_default().get_default_database(),
+ this.db_dir,
+ false
+ );
+ assert_pinning(test_article2, cert1, id, false);
+ assert_pinning(test_article2, cert2, id, false);
+ }
+
+ public void database_disk_certificate_pinning_without_gcr()
+ throws GLib.Error {
+ var test_article1 = new Application.TlsDatabase(
+ GLib.TlsBackend.get_default().get_default_database(),
+ this.db_dir,
+ false
+ );
+ var id = new_identity();
+ var cert1 = new_cert("cert1");
+ var cert2 = new_cert("cert2");
+
+ // Assert the db doesn't know about the cert first up.
+ assert_pinning(test_article1, cert1, id, false);
+ assert_pinning(test_article1, cert2, id, false);
+
+ // Pin a cert in the db
+ test_article1.pin_certificate.begin(
+ cert1, id, true, null, this.async_completion
+ );
+ test_article1.pin_certificate.end(this.async_result());
+
+ // Assert the db now knows about it, but not the other
+ assert_pinning(test_article1, cert1, id, true);
+ assert_pinning(test_article1, cert2, id, false);
+
+ // Construct a new test article and ensure it has loaded the
+ // first from disk
+ var test_article2 = new Application.TlsDatabase(
+ GLib.TlsBackend.get_default().get_default_database(),
+ this.db_dir,
+ false
+ );
+ assert_pinning(test_article2, cert1, id, true);
+ assert_pinning(test_article2, cert2, id, false);
+ }
+
+ private void assert_pinning(Application.TlsDatabase db,
+ GLib.TlsCertificate cert,
+ GLib.SocketConnectable id,
+ bool is_pinned)
+ throws GLib.Error {
+ // Test both the sync and async calls to ensure equivalence
+ var sync_ret = db.verify_chain(
+ cert,
+ GLib.TlsDatabase.PURPOSE_AUTHENTICATE_SERVER,
+ id,
+ null,
+ NONE,
+ null
+ );
+ if (is_pinned) {
+ assert_true(
+ sync_ret == 0,
+ "is pinned sync"
+ );
+ } else {
+ assert_true(
+ sync_ret == GLib.TlsCertificateFlags.UNKNOWN_CA,
+ "not pinned sync"
+ );
+ }
+
+ db.verify_chain_async.begin(
+ cert,
+ GLib.TlsDatabase.PURPOSE_AUTHENTICATE_SERVER,
+ id,
+ null,
+ NONE,
+ null,
+ this.async_completion
+ );
+ var async_ret = db.verify_chain_async.end(this.async_result());
+ if (is_pinned) {
+ assert_true(
+ async_ret == 0,
+ "is pinned async"
+ );
+ } else {
+ assert_true(
+ async_ret == GLib.TlsCertificateFlags.UNKNOWN_CA,
+ "not pinned async"
+ );
+ }
+ }
+
+ private GLib.SocketConnectable new_identity() {
+ return new GLib.NetworkAddress(IDENITY_HOSTNAME, IDENITY_PORT);
+ }
+
+ private GLib.TlsCertificate new_cert(string name) throws GLib.Error {
+ var priv_name = name + ".priv";
+ var cert_name = name + ".cert";
+ var template_name = name + ".template";
+ GLib.Process.spawn_sync(
+ this.cert_dir.get_path(),
+ {
+ "certtool", "--generate-privkey", "--outfile", priv_name
+ },
+ GLib.Environ.get(),
+ SpawnFlags.SEARCH_PATH,
+ null
+ );
+ this.cert_dir.get_child(template_name).create(NONE).write("""
+organization = "Example Inc."
+country = AU
+serial = %d
+expiration_days = 1
+dns_name = "%s"
+encryption_key
+""".printf(CertificateManagerTest.cert_id++, IDENITY_HOSTNAME).data);
+
+ GLib.Process.spawn_sync(
+ this.cert_dir.get_path(),
+ {
+ "certtool",
+ "--generate-self-signed",
+ "--load-privkey", priv_name,
+ "--template", template_name,
+ "--outfile", cert_name
+ },
+ GLib.Environ.get(),
+ SpawnFlags.SEARCH_PATH,
+ null
+ );
+ return new GLib.TlsCertificate.from_file(
+ this.cert_dir.get_child(cert_name).get_path()
+ );
+ }
+
+}
diff --git a/test/meson.build b/test/meson.build
index 9cd4717f7..3b04847b1 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -83,6 +83,7 @@ geary_test_client_sources = [
'engine/api/geary-credentials-mediator-mock.vala',
'client/accounts/accounts-manager-test.vala',
+ 'client/application/application-certificate-manager-test.vala',
'client/application/application-client-test.vala',
'client/application/application-configuration-test.vala',
'client/components/client-web-view-test.vala',
diff --git a/test/test-client.vala b/test/test-client.vala
index 1016d2a53..c7b6a2f0f 100644
--- a/test/test-client.vala
+++ b/test/test-client.vala
@@ -49,6 +49,7 @@ int main(string[] args) {
// Keep this before other ClientWebView based tests since it tests
// WebContext init
client.add_suite(new Accounts.ManagerTest().get_suite());
+ client.add_suite(new Application.CertificateManagerTest().suite);
client.add_suite(new Application.ClientTest().get_suite());
client.add_suite(new Application.ConfigurationTest().get_suite());
client.add_suite(new ClientWebViewTest().get_suite());
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]