[gnome-keyring] [egg] Complete implementation of openssl sytle PEM writing



commit a78de9fffce4ef2ac669e389b2283e0efbb6feb2
Author: Stef Walter <stefw collabora co uk>
Date:   Tue Mar 8 18:10:19 2011 +0100

    [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        |   46 +++++++++++++++++++++++++++++-----------------
 egg/tests/test-openssl.c |   29 +++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 17 deletions(-)
---
diff --git a/egg/egg-openssl.c b/egg/egg-openssl.c
index ef715dd..2e83e63 100644
--- a/egg/egg-openssl.c
+++ b/egg/egg-openssl.c
@@ -284,13 +284,11 @@ 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)
 {
 	GString *string = (GString*)user_data;
-	
+
 	g_string_append (string, (gchar*)key);
 	g_string_append (string, ": ");
 	g_string_append (string, (gchar*)value);
@@ -303,14 +301,15 @@ 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);
 	g_return_val_if_fail (n_result, NULL);
 
 	string = g_string_sized_new (4096);
-	
+
 	/* The prefix */
 	g_string_append_len (string, PEM_PREF_BEGIN, PEM_PREF_BEGIN_L);
 	g_string_append (string, g_quark_to_string (type));
@@ -324,29 +323,42 @@ 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);
-	
-	/* The actual base64 data */
+	g_string_set_size (string, n_prefix + estimate);
+
+	/* 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);
 	g_string_append_c (string, '\n');
-	
+
 	*n_result = string->len;
 	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 <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 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 ();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]