glib/gio/tests/tls-certificate.c
Fredrik Ternerot a437a50694 gtlscertificate: Allow any type of private key in PEM files
Allow any type of private key in PEM files by treating PEM guards ending
with "PRIVATE KEY-----" as a private key instead of looking for a
pre-defined set of PEM guards. This enables the possibility for custom
GTlsBackend to add support for new key types.

Test cases have been expanded to ensure PEM parsing works for private
key when either header or footer is missing.

Encrypted PKCS#8 is still rejected. Test case has been added for this to
ensure behaviour is the same before and after this change.
2018-12-18 11:43:08 +01:00

480 lines
16 KiB
C

/* GLib testing framework examples and 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.1 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, see <http://www.gnu.org/licenses/>.
*
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*/
#include "config.h"
#include <gio/gio.h>
#include "gtesttlsbackend.h"
typedef struct
{
gchar *cert_pems[3];
gchar *cert_crlf_pem;
gchar *key_pem;
gchar *key_crlf_pem;
gchar *key8_pem;
} Reference;
static void
pem_parser (const Reference *ref)
{
GTlsCertificate *cert;
gchar *pem;
gsize pem_len = 0;
gchar *parsed_cert_pem = NULL;
const gchar *parsed_key_pem = NULL;
GError *error = NULL;
/* Check PEM parsing in certificate, private key order. */
g_file_get_contents (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert-key.pem", NULL), &pem, &pem_len, &error);
g_assert_no_error (error);
g_assert_nonnull (pem);
g_assert_cmpuint (pem_len, >=, 10);
cert = g_tls_certificate_new_from_pem (pem, -1, &error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_cmpstr (parsed_key_pem, ==, ref->key_pem);
parsed_key_pem = NULL;
g_object_unref (cert);
/* Make sure length is respected and parser detect invalid PEM
* when cert is truncated. */
cert = g_tls_certificate_new_from_pem (pem, 10, &error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
/* Make sure length is respected and parser detect invalid PEM
* when cert exists but key is truncated. */
cert = g_tls_certificate_new_from_pem (pem, pem_len - 10, &error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_free (pem);
/* Check PEM parsing in private key, certificate order */
g_file_get_contents (g_test_get_filename (G_TEST_DIST, "cert-tests", "key-cert.pem", NULL), &pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (pem);
cert = g_tls_certificate_new_from_pem (pem, -1, &error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_cmpstr (parsed_key_pem, ==, ref->key_pem);
parsed_key_pem = NULL;
g_free (pem);
g_object_unref (cert);
/* Check certificate only PEM */
g_file_get_contents (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL), &pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (pem);
cert = g_tls_certificate_new_from_pem (pem, -1, &error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_null (parsed_key_pem);
g_free (pem);
g_object_unref (cert);
/* Check error with private key only PEM */
g_file_get_contents (g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL), &pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (pem);
cert = g_tls_certificate_new_from_pem (pem, -1, &error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
g_free (pem);
}
static void
pem_parser_handles_chain (const Reference *ref)
{
GTlsCertificate *cert;
GTlsCertificate *issuer;
GTlsCertificate *original_cert;
gchar *pem;
gchar *parsed_cert_pem = NULL;
const gchar *parsed_key_pem = NULL;
GError *error = NULL;
/* Check that a chain with exactly three certificates is returned */
g_file_get_contents (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert-list.pem", NULL), &pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (pem);
cert = original_cert = g_tls_certificate_new_from_pem (pem, -1, &error);
g_free (pem);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_clear_pointer (&parsed_cert_pem, g_free);
/* Make sure the private key was parsed */
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_key_pem, ==, ref->key_pem);
parsed_key_pem = NULL;
/* Now test the second cert */
issuer = g_tls_certificate_get_issuer (cert);
g_assert_nonnull (issuer);
cert = issuer;
issuer = g_tls_certificate_get_issuer (cert);
g_assert_nonnull (issuer);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[1]);
g_clear_pointer (&parsed_cert_pem, g_free);
/* Only the first cert should have a private key */
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_null (parsed_key_pem);
/* Now test the final cert */
cert = issuer;
issuer = g_tls_certificate_get_issuer (cert);
g_assert_null (issuer);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[2]);
g_clear_pointer (&parsed_cert_pem, g_free);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_null (parsed_key_pem);
g_object_unref (original_cert);
}
static void
from_file (const Reference *ref)
{
GTlsCertificate *cert;
gchar *parsed_cert_pem = NULL;
const gchar *parsed_key_pem = NULL;
GError *error = NULL;
cert = g_tls_certificate_new_from_file (g_test_get_filename (G_TEST_DIST, "cert-tests", "key-cert.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_cmpstr (parsed_key_pem, ==, ref->key_pem);
parsed_key_pem = NULL;
g_object_unref (cert);
}
static void
from_files (const Reference *ref)
{
GTlsCertificate *cert;
gchar *parsed_cert_pem = NULL;
const gchar *parsed_key_pem = NULL;
GError *error = NULL;
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_cmpstr (parsed_key_pem, ==, ref->key_pem);
parsed_key_pem = NULL;
g_object_unref (cert);
/* Missing private key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "cert2.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
/* Missing header private key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key_missing-header.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
/* Missing footer private key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key_missing-footer.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
/* Missing certificate */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
/* Using this method twice with a file containing both private key and
* certificate as a way to inforce private key presence is a fair use
*/
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "key-cert.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key-cert.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_unref (cert);
}
static void
from_files_crlf (const Reference *ref)
{
GTlsCertificate *cert;
gchar *parsed_cert_pem = NULL;
const gchar *parsed_key_pem = NULL;
GError *error = NULL;
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert-crlf.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key-crlf.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_crlf_pem);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_cmpstr (parsed_key_pem, ==, ref->key_crlf_pem);
parsed_key_pem = NULL;
g_object_unref (cert);
}
static void
from_files_pkcs8 (const Reference *ref)
{
GTlsCertificate *cert;
gchar *parsed_cert_pem = NULL;
const gchar *parsed_key_pem = NULL;
GError *error = NULL;
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key8.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_nonnull (cert);
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
parsed_key_pem = g_test_tls_connection_get_private_key_pem (cert);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[0]);
g_free (parsed_cert_pem);
parsed_cert_pem = NULL;
g_assert_cmpstr (parsed_key_pem, ==, ref->key8_pem);
parsed_key_pem = NULL;
g_object_unref (cert);
}
static void
from_files_pkcs8enc (const Reference *ref)
{
GTlsCertificate *cert;
GError *error = NULL;
/* Mare sure an error is returned for encrypted key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key8enc.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
}
static void
list_from_file (const Reference *ref)
{
GList *list, *l;
GError *error = NULL;
int i;
list = g_tls_certificate_list_new_from_file (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert-list.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_cmpint (g_list_length (list), ==, 3);
l = list;
for (i = 0; i < 3; i++)
{
GTlsCertificate *cert = l->data;
gchar *parsed_cert_pem = NULL;
g_object_get (cert,
"certificate-pem", &parsed_cert_pem,
NULL);
g_assert_cmpstr (parsed_cert_pem, ==, ref->cert_pems[i]);
g_free (parsed_cert_pem);
l = g_list_next (l);
}
g_list_free_full (list, g_object_unref);
/* Empty list is not an error */
list = g_tls_certificate_list_new_from_file (g_test_get_filename (G_TEST_DIST, "cert-tests", "nothing.pem", NULL),
&error);
g_assert_no_error (error);
g_assert_cmpint (g_list_length (list), ==, 0);
}
int
main (int argc,
char *argv[])
{
int rtv;
Reference ref;
GError *error = NULL;
gchar *path;
g_test_init (&argc, &argv, NULL);
_g_test_tls_backend_get_type ();
/* Load reference PEM */
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL);
g_file_get_contents (path, &ref.cert_pems[0], NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.cert_pems[0]);
g_free (path);
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "cert2.pem", NULL);
g_file_get_contents (path, &ref.cert_pems[1], NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.cert_pems[1]);
g_free (path);
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "cert3.pem", NULL);
g_file_get_contents (path, &ref.cert_pems[2], NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.cert_pems[2]);
g_free (path);
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "cert-crlf.pem", NULL);
g_file_get_contents (path, &ref.cert_crlf_pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.cert_crlf_pem);
g_free (path);
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL);
g_file_get_contents (path, &ref.key_pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.key_pem);
g_free (path);
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "key-crlf.pem", NULL);
g_file_get_contents (path, &ref.key_crlf_pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.key_crlf_pem);
g_free (path);
path = g_test_build_filename (G_TEST_DIST, "cert-tests", "key8.pem", NULL);
g_file_get_contents (path, &ref.key8_pem, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (ref.key8_pem);
g_free (path);
g_test_add_data_func ("/tls-certificate/pem-parser",
&ref, (GTestDataFunc)pem_parser);
g_test_add_data_func ("/tls-certificate/pem-parser-handles-chain",
&ref, (GTestDataFunc)pem_parser_handles_chain);
g_test_add_data_func ("/tls-certificate/from_file",
&ref, (GTestDataFunc)from_file);
g_test_add_data_func ("/tls-certificate/from_files",
&ref, (GTestDataFunc)from_files);
g_test_add_data_func ("/tls-certificate/from_files_crlf",
&ref, (GTestDataFunc)from_files_crlf);
g_test_add_data_func ("/tls-certificate/from_files_pkcs8",
&ref, (GTestDataFunc)from_files_pkcs8);
g_test_add_data_func ("/tls-certificate/from_files_pkcs8enc",
&ref, (GTestDataFunc)from_files_pkcs8enc);
g_test_add_data_func ("/tls-certificate/list_from_file",
&ref, (GTestDataFunc)list_from_file);
rtv = g_test_run();
g_free (ref.cert_pems[0]);
g_free (ref.cert_pems[1]);
g_free (ref.cert_pems[2]);
g_free (ref.cert_crlf_pem);
g_free (ref.key_pem);
g_free (ref.key_crlf_pem);
g_free (ref.key8_pem);
return rtv;
}