[glib-networking/wip/tingping/serialize] gnutls: Implement serialization
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/tingping/serialize] gnutls: Implement serialization
- Date: Mon, 17 Jun 2019 10:36:44 +0000 (UTC)
commit 47a3a7a32128133c773dbce397ab176dd752c816
Author: Patrick Griffis <pgriffis igalia com>
Date: Mon Jun 17 03:35:25 2019 -0700
gnutls: Implement serialization
tls/gnutls/gtlscertificate-gnutls.c | 170 ++++++++++++++++++++++++++++++++++++
tls/tests/certificate.c | 40 ++++++++-
2 files changed, 209 insertions(+), 1 deletion(-)
---
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index ff18c46..bfd367e 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -359,6 +359,172 @@ g_tls_certificate_gnutls_verify (GTlsCertificate *cert,
return gtls_flags;
}
+#if GLIB_CHECK_VERSION (2, 61, 1)
+static GVariant *
+serialize_single_cert (GTlsCertificateGnutls *cert,
+ GError **error)
+{
+ GVariantDict *dict;
+ gnutls_datum_t cert_data;
+ int gnutls_err;
+
+ if ((gnutls_err = gnutls_x509_crt_export2 (cert->cert, GNUTLS_X509_FMT_DER, &cert_data)) !=
GNUTLS_E_SUCCESS)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to export certificate (%d) (%s)",
gnutls_err, gnutls_strerror (gnutls_err));
+ return NULL;
+ }
+
+ dict = g_variant_dict_new (NULL);
+ g_variant_dict_insert_value (dict, "certificate", g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
cert_data.data, cert_data.size, sizeof (guchar)));
+ gnutls_free (cert_data.data);
+
+ if (g_tls_certificate_gnutls_has_key (cert))
+ {
+ gnutls_datum_t privkey_data;
+
+ /* NOTE: This can be encrypted by password in the future */
+ if ((gnutls_err = gnutls_x509_privkey_export2_pkcs8 (cert->key, GNUTLS_X509_FMT_DER, NULL,
GNUTLS_PKCS_PLAIN, &privkey_data)) != GNUTLS_E_SUCCESS)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to export private-key (%d) (%s)",
gnutls_err, gnutls_strerror (gnutls_err));
+ g_variant_dict_unref (dict);
+ return NULL;
+ }
+
+ g_variant_dict_insert_value (dict, "private-key", g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
privkey_data.data, privkey_data.size, sizeof (guchar)));
+ gnutls_free (privkey_data.data);
+ }
+
+ if (cert->issuer != NULL)
+ {
+ GVariant *serialized_cert = serialize_single_cert (cert->issuer, error);
+ if (serialized_cert == NULL)
+ {
+ g_variant_dict_unref (dict);
+ return NULL;
+ }
+ g_variant_dict_insert_value (dict, "issuer", serialized_cert);
+ }
+
+ return g_variant_dict_end (dict);
+}
+
+#define CERTIFICATE_SERIALIZATION_FORMAT 1
+
+static GVariant *
+g_tls_certificate_gnutls_serialize (GTlsCertificate *cert,
+ GError **error)
+{
+ GVariantDict *dict = g_variant_dict_new (NULL);
+ GVariant *serialized_cert;
+
+ /* Metadata to sanely change formats and handle cross-backend serializing */
+ g_variant_dict_insert (dict, "tls-backend", "s", "gnutls");
+ g_variant_dict_insert (dict, "tls-backend-format", "q", CERTIFICATE_SERIALIZATION_FORMAT);
+
+ serialized_cert = serialize_single_cert (G_TLS_CERTIFICATE_GNUTLS (cert), error);
+ if (serialized_cert == NULL)
+ {
+ g_variant_dict_unref (dict);
+ return NULL;
+ }
+
+ g_variant_dict_insert_value (dict, "certificate", serialized_cert);
+ return g_variant_dict_end (dict);
+}
+
+static GTlsCertificate *
+deserialize_single_certificate (GVariant *serialized_cert,
+ GError **error)
+{
+ GVariant *certificate_value;
+ gsize certificate_size;
+ GVariant *privkey_value;
+ GTlsCertificate *cert = NULL;
+ GTlsCertificate *issuer_cert = NULL;
+ GVariant *issuer;
+ gnutls_datum_t certificate_data;
+
+ certificate_value = g_variant_lookup_value (serialized_cert, "certificate", G_VARIANT_TYPE_ARRAY);
+ if (certificate_value == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to deserialize certificate (missing certificate)");
+ return NULL;
+ }
+
+ certificate_data.data = (guchar*) g_variant_get_fixed_array (certificate_value, &certificate_size, sizeof
(guchar));
+ certificate_data.size = (int) certificate_size;
+
+ if ((issuer = g_variant_lookup_value (serialized_cert, "issuer", G_VARIANT_TYPE_VARDICT)))
+ {
+ issuer_cert = deserialize_single_certificate (issuer, error);
+ if (issuer_cert == NULL)
+ return NULL;
+ }
+
+ cert = g_tls_certificate_gnutls_new (&certificate_data, issuer_cert);
+
+ privkey_value = g_variant_lookup_value (serialized_cert, "private-key", G_VARIANT_TYPE_ARRAY);
+ if (privkey_value != NULL)
+ {
+ GTlsCertificateGnutls *gnutls = (GTlsCertificateGnutls*) cert;
+ gnutls_datum_t privkey_data;
+ gsize privkey_size;
+ int status;
+
+ privkey_data.data = (guchar*) g_variant_get_fixed_array (privkey_value, &privkey_size, sizeof
(guchar));
+ privkey_data.size = (int) privkey_size;
+
+ gnutls_x509_privkey_init (&gnutls->key);
+
+ status = gnutls_x509_privkey_import_pkcs8 (gnutls->key, &privkey_data,
+ GNUTLS_X509_FMT_DER, NULL,
+ GNUTLS_PKCS_PLAIN);
+ if (status == GNUTLS_E_SUCCESS)
+ gnutls->have_key = TRUE;
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to deserialize private-key: %s",
+ gnutls_strerror (status));
+ g_object_unref (cert);
+ return NULL;
+ }
+ }
+
+ return cert;
+}
+
+static GTlsCertificate *
+g_tls_certificate_gnutls_deserialize (GVariant *serialized_cert,
+ GError **error)
+{
+ GVariant *cert;
+ char *backend = NULL;
+ guint16 format = 0;
+
+ if (!g_variant_lookup (serialized_cert, "tls-backend", "s", &backend) ||
+ !g_variant_lookup (serialized_cert, "tls-backend-format", "q", &format) ||
+ g_strcmp0 (backend, "gnutls") != 0 ||
+ format > CERTIFICATE_SERIALIZATION_FORMAT)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to deserialize certificate (unknown format
(%s - %d))", backend ? backend : "missing", format);
+ g_free (backend);
+ return NULL;
+ }
+ g_free (backend);
+
+ cert = g_variant_lookup_value (serialized_cert, "certificate", G_VARIANT_TYPE_VARDICT);
+ if (cert == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to deserialize certificate (missing
certificate)");
+ return NULL;
+ }
+
+ return deserialize_single_certificate (cert, error);
+}
+#endif
+
static void
g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
{
@@ -370,6 +536,10 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
gobject_class->finalize = g_tls_certificate_gnutls_finalize;
certificate_class->verify = g_tls_certificate_gnutls_verify;
+#if GLIB_CHECK_VERSION (2, 61, 1)
+ certificate_class->serialize = g_tls_certificate_gnutls_serialize;
+ certificate_class->deserialize = g_tls_certificate_gnutls_deserialize;
+#endif
g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
diff --git a/tls/tests/certificate.c b/tls/tests/certificate.c
index 81e2ebb..4444b23 100644
--- a/tls/tests/certificate.c
+++ b/tls/tests/certificate.c
@@ -322,6 +322,44 @@ test_create_list_bad (void)
g_error_free (error);
}
+static void
+test_certificate_serialize (void)
+{
+#if GLIB_CHECK_VERSION (2, 61, 1)
+
+ GTlsCertificate *cert, *deserialized;
+ GTlsCertificate *issuer, *deserialized_issuer;
+ GError *error = NULL;
+ GVariant *serialized;
+
+ cert = g_tls_certificate_new_from_file (tls_test_file_path ("chain.pem"), &error);
+ g_assert_no_error (error);
+ g_assert_true (G_IS_TLS_CERTIFICATE (cert));
+
+ serialized = g_tls_certificate_serialize (cert, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (serialized);
+
+ deserialized = g_tls_certificate_deserialize (serialized, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (deserialized);
+
+ /* Note this doesn't test private keys but should be good enough */
+ g_assert_true (g_tls_certificate_is_same (cert, deserialized));
+
+ /* Ensure the full chain is intact */
+ issuer = g_tls_certificate_get_issuer (cert);
+ deserialized_issuer = g_tls_certificate_get_issuer (deserialized);
+ g_assert_true (g_tls_certificate_is_same (issuer, deserialized_issuer));
+
+ g_object_unref (cert);
+ g_object_unref (deserialized);
+ g_variant_unref (serialized);
+#else
+ g_test_skip ("GLib too old for g_tls_certificate_serialize()");
+#endif
+}
+
/* -----------------------------------------------------------------------------
* CERTIFICATE VERIFY
*/
@@ -557,7 +595,6 @@ main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
-
g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
g_setenv ("GIO_USE_TLS", BACKEND, TRUE);
g_assert (g_ascii_strcasecmp (G_OBJECT_TYPE_NAME (g_tls_backend_get_default ()), "GTlsBackend" BACKEND) ==
0);
@@ -579,6 +616,7 @@ main (int argc,
g_test_add_func ("/tls/" BACKEND "/certificate/create-no-chain", test_create_certificate_no_chain);
g_test_add_func ("/tls/" BACKEND "/certificate/create-list", test_create_list);
g_test_add_func ("/tls/" BACKEND "/certificate/create-list-bad", test_create_list_bad);
+ g_test_add_func ("/tls/" BACKEND "/certificate/serialize", test_certificate_serialize);
g_test_add ("/tls/" BACKEND "/certificate/verify-good", TestVerify, NULL,
setup_verify, test_verify_certificate_good, teardown_verify);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]