From 810500a36fa44305cb36fa8b4862473b5c1f38f4 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 8 Mar 2011 18:10:19 +0100 Subject: [PATCH] [egg] Complete implementation of openssl sytle PEM writing The openssl PEM parser is particularly fragile, so write some stringent tests to check. --- egg/egg-openssl.c | 34 +++++++++++++++++++++++----------- egg/tests/test-openssl.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/egg/egg-openssl.c b/egg/egg-openssl.c index bf9c487..001e61e 100644 --- a/egg/egg-openssl.c +++ b/egg/egg-openssl.c @@ -284,8 +284,6 @@ egg_openssl_pem_parse (const guchar *data, gsize n_data, return nfound; } -#ifdef UNTESTED_CODE - static void append_each_header (gpointer key, gpointer value, gpointer user_data) { @@ -303,7 +301,8 @@ egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type, { GString *string; gint state, save; - gsize length, n_prefix; + gsize i, length; + gsize n_prefix, estimate; g_return_val_if_fail (data || !n_data, NULL); g_return_val_if_fail (type, NULL); @@ -324,18 +323,33 @@ egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type, } /* Resize string to fit the base64 data. Algorithm from Glib reference */ - length = n_data * 4 / 3 + n_data * 4 / (3 * 72) + 7; + estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7; n_prefix = string->len; - g_string_set_size (string, n_prefix + length); + g_string_set_size (string, n_prefix + estimate); - /* The actual base64 data */ + /* The actual base64 data, without line breaks */ state = save = 0; - length = g_base64_encode_step (data, n_data, TRUE, - string->str + string->len, &state, &save); + length = g_base64_encode_step (data, n_data, FALSE, + string->str + n_prefix, &state, &save); + length += g_base64_encode_close (TRUE, string->str + n_prefix + length, + &state, &save); + + g_assert (length <= estimate); g_string_set_size (string, n_prefix + length); + /* + * OpenSSL is absolutely certain that it wants its PEM base64 + * lines to be 64 characters in length. So go through and break + * those lines up. + */ + + for (i = 64; i < length; i += 64) { + g_string_insert_c (string, n_prefix + i, '\n'); + ++length; + ++i; + } + /* The suffix */ - g_string_append_c (string, '\n'); g_string_append_len (string, PEM_PREF_END, PEM_PREF_END_L); g_string_append (string, g_quark_to_string (type)); g_string_append_len (string, PEM_SUFF, PEM_SUFF_L); @@ -345,8 +359,6 @@ egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type, return (guchar*)g_string_free (string, FALSE); } -#endif /* UNTESTED_CODE */ - /* ---------------------------------------------------------------------------- * DEFINITIONS */ diff --git a/egg/tests/test-openssl.c b/egg/tests/test-openssl.c index bf8134a..18f9fd9 100644 --- a/egg/tests/test-openssl.c +++ b/egg/tests/test-openssl.c @@ -33,12 +33,14 @@ #include #include #include +#include EGG_SECURE_GLIB_DEFINITIONS (); typedef struct { guchar *input; gsize n_input; + GQuark reftype; guchar *refenc; guchar *refdata; gsize n_refenc; @@ -76,6 +78,9 @@ parse_reference (GQuark type, const guchar *data, gsize n_data, gboolean res; const gchar *dekinfo; + g_assert (type); + test->reftype = type; + g_assert ("no data in PEM callback" && data != NULL); g_assert ("no data in PEM callback" && n_data > 0); test->refenc = g_memdup (data, n_data); @@ -129,6 +134,29 @@ test_write_reference (Test *test, gconstpointer unused) g_assert ("data doesn't match input" && memcmp (encrypted, test->refenc, n_encrypted) == 0); } +static void +test_write_exactly_same (Test *test, gconstpointer unused) +{ + guchar *result; + gsize n_result; + guint num; + + num = egg_openssl_pem_parse (test->input, test->n_input, parse_reference, test); + g_assert ("couldn't PEM block in reference data" && num == 1); + + result = egg_openssl_pem_write (test->refenc, test->n_refenc, test->reftype, + test->refheaders, &n_result); + + /* + * Yes sirrr. Openssl's parser is so fragile, that we have to make it + * character for character identical. This includes line breaks, whitespace + * and line endings. + */ + + egg_assert_cmpmem (test->input, test->n_input, ==, result, n_result); + g_free (result); +} + /* 29 bytes (prime number, so block length has bad chance of matching */ static const guchar *TEST_DATA = (guchar*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ123"; const gsize TEST_DATA_L = 29; @@ -175,6 +203,7 @@ main (int argc, char **argv) g_test_add ("/openssl/parse_reference", Test, NULL, setup, test_parse_reference, teardown); g_test_add ("/openssl/write_reference", Test, NULL, setup, test_write_reference, teardown); + g_test_add ("/openssl/write_exactly_same", Test, NULL, setup, test_write_exactly_same, teardown); g_test_add ("/openssl/openssl_roundtrip", Test, NULL, setup, test_openssl_roundtrip, teardown); return g_test_run (); -- 2.7.4