[gnome-keyring] gcr: Implement recognizing of OpenPGP packets



commit 1e36c52404938d521ce3cb11632f393689308ac6
Author: Stef Walter <stefw collabora co uk>
Date:   Tue Sep 13 17:07:28 2011 +0200

    gcr: Implement recognizing of OpenPGP packets
    
     * Doesn't actually parse keys or anything else about OpenPGP (yet).
     * Generalize the PEM code so that it works for PGP armor.

 .gitignore                            |    1 +
 docs/reference/gcr/gcr-sections.txt   |    1 +
 egg/Makefile.am                       |    1 +
 egg/egg-armor.c                       |  407 +++++++++++++++++++++++++++++++++
 egg/egg-armor.h                       |   50 ++++
 egg/egg-openssl.c                     |  350 ----------------------------
 egg/egg-openssl.h                     |   18 --
 egg/tests/test-openssl.c              |   17 +-
 gcr/Makefile.am                       |    1 +
 gcr/gcr-certificate-exporter.c        |    7 +-
 gcr/gcr-openpgp.c                     |  226 ++++++++++++++++++
 gcr/gcr-openpgp.h                     |   49 ++++
 gcr/gcr-parser.c                      |   87 ++++++-
 gcr/gcr-types.h                       |    3 +
 gcr/tests/Makefile.am                 |    1 +
 gcr/tests/files/pubring.gpg           |  Bin 0 -> 21969 bytes
 gcr/tests/files/werner-koch.asc       |   71 ++++++
 gcr/tests/test-openpgp.c              |   94 ++++++++
 pkcs11/gkm/tests/test-data-der.c      |    3 +-
 pkcs11/roots-store/gkm-roots-module.c |    3 +-
 pkcs11/ssh-store/gkm-ssh-openssh.c    |    5 +-
 21 files changed, 999 insertions(+), 396 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index d569cf7..35ab761 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,7 @@ run-tests
 /gcr/tests/test-gnupg-key
 /gcr/tests/test-gnupg-process
 /gcr/tests/test-memory-icon
+/gcr/tests/test-openpgp
 /gcr/tests/test-openssh
 /gcr/tests/test-parser
 /gcr/tests/test-pkcs11-certificate
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index dfb22ab..ea40ee1 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -590,4 +590,5 @@ GcrMemoryIconPrivate
 GCR_ERROR
 gcr_error_get_domain
 GcrOpensshPubCallback
+GcrOpenpgpCallback
 </SECTION>
diff --git a/egg/Makefile.am b/egg/Makefile.am
index 917d884..4b61830 100644
--- a/egg/Makefile.am
+++ b/egg/Makefile.am
@@ -23,6 +23,7 @@ libegg_la_CFLAGS = \
 	$(GLIB_CFLAGS)
 
 libegg_la_SOURCES = \
+	egg-armor.c egg-armor.h \
 	egg-asn1x.c egg-asn1x.h \
 	egg-buffer.c egg-buffer.h \
 	egg-byte-array.c egg-byte-array.h \
diff --git a/egg/egg-armor.c b/egg/egg-armor.c
new file mode 100644
index 0000000..5a4b047
--- /dev/null
+++ b/egg/egg-armor.c
@@ -0,0 +1,407 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-openssl.c - OpenSSL compatibility functionality
+
+   Copyright (C) 2007 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "egg-hex.h"
+#include "egg-armor.h"
+#include "egg-secure-memory.h"
+
+#include <gcrypt.h>
+
+#include <glib.h>
+
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * Armor looks like:
+ *
+ * 	-----BEGIN RSA PRIVATE KEY-----
+ * 	Proc-Type: 4,ENCRYPTED
+ * 	DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
+ *
+ * 	4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
+ * 	Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
+ *	u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
+ *	................................................................
+ * 	=on29
+ * 	-----END RSA PRIVATE KEY-----
+ *
+ * The last line before END is an option OpenPGP armor checksum
+ */
+
+EGG_SECURE_DECLARE (armor);
+
+#define ARMOR_SUFF          "-----"
+#define ARMOR_SUFF_L        5
+#define ARMOR_PREF_BEGIN    "-----BEGIN "
+#define ARMOR_PREF_BEGIN_L  11
+#define ARMOR_PREF_END      "-----END "
+#define ARMOR_PREF_END_L    9
+
+static void
+parse_header_lines (const gchar *hbeg,
+                    const gchar *hend,
+                    GHashTable **result)
+{
+	gchar **lines, **l;
+	gchar *line, *name, *value;
+	gchar *copy;
+
+	copy = g_strndup (hbeg, hend - hbeg);
+	lines = g_strsplit (copy, "\n", 0);
+	g_free (copy);
+
+	for (l = lines; l && *l; ++l) {
+		line = *l;
+		g_strstrip (line);
+
+		/* Look for the break between name: value */
+		value = strchr (line, ':');
+		if (value == NULL)
+			continue;
+
+		*value = 0;
+		value = g_strdup (value + 1);
+		g_strstrip (value);
+
+		name = g_strdup (line);
+		g_strstrip (name);
+
+		if (!*result)
+			*result = egg_armor_headers_new ();
+		g_hash_table_replace (*result, name, value);
+	}
+
+	g_strfreev (lines);
+}
+
+static const gchar*
+armor_find_begin (const gchar *data,
+                  gsize n_data,
+                  GQuark *type,
+                  const gchar **outer)
+{
+	const gchar *pref, *suff;
+	gchar *stype;
+
+	/* Look for a prefix */
+	pref = g_strstr_len ((gchar*)data, n_data, ARMOR_PREF_BEGIN);
+	if (!pref)
+		return NULL;
+
+	n_data -= (pref - data) + ARMOR_PREF_BEGIN_L;
+	data = pref + ARMOR_PREF_BEGIN_L;
+
+	/* Look for the end of that begin */
+	suff = g_strstr_len ((gchar*)data, n_data, ARMOR_SUFF);
+	if (!suff)
+		return NULL;
+
+	/* Make sure on the same line */
+	if (memchr (pref, '\n', suff - pref))
+		return NULL;
+
+	if (outer)
+		*outer = pref;
+
+	if (type) {
+		*type = 0;
+		pref += ARMOR_PREF_BEGIN_L;
+		g_assert (suff > pref);
+		stype = g_alloca (suff - pref + 1);
+		memcpy (stype, pref, suff - pref);
+		stype[suff - pref] = 0;
+		*type = g_quark_from_string (stype);
+	}
+
+	/* The byte after this ---BEGIN--- */
+	return suff + ARMOR_SUFF_L;
+}
+
+static const gchar*
+armor_find_end (const gchar *data,
+                gsize n_data,
+                GQuark type,
+                const gchar **outer)
+{
+	const gchar *stype;
+	const gchar *pref;
+	const gchar *line;
+	gsize n_type;
+
+	/* Look for a prefix */
+	pref = g_strstr_len (data, n_data, ARMOR_PREF_END);
+	if (!pref)
+		return NULL;
+
+	n_data -= (pref - data) + ARMOR_PREF_END_L;
+	data = pref + ARMOR_PREF_END_L;
+
+	/* Next comes the type string */
+	stype = g_quark_to_string (type);
+	n_type = strlen (stype);
+	if (strncmp ((gchar*)data, stype, n_type) != 0)
+		return NULL;
+
+	n_data -= n_type;
+	data += n_type;
+
+	/* Next comes the suffix */
+	if (strncmp ((gchar*)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0)
+		return NULL;
+
+	/*
+	 * Check if there's a OpenPGP style armor checksum line. OpenPGP
+	 * does not insist that we validate this line, and is more useful
+	 * for PGP messages, rather than the keys we usually see.
+	 */
+	line = memrchr (data, '\n', (pref - 1) - data);
+	if (line && line[1] == '=')
+		pref = line;
+
+	if (outer != NULL) {
+		data += ARMOR_SUFF_L;
+		if (isspace (data[0]))
+			data++;
+		*outer = data;
+	}
+
+	/* The end of the data */
+	return pref;
+}
+
+static gboolean
+armor_parse_block (const gchar *data,
+                   gsize n_data,
+                   guchar **decoded,
+                   gsize *n_decoded,
+                   GHashTable **headers)
+{
+	const gchar *x, *hbeg, *hend;
+	const gchar *p, *end;
+	gint state = 0;
+	guint save = 0;
+
+	g_assert (data);
+	g_assert (n_data);
+
+	g_assert (decoded);
+	g_assert (n_decoded);
+
+	p = data;
+	end = p + n_data;
+
+	hbeg = hend = NULL;
+
+	/* Try and find a pair of blank lines with only white space between */
+	while (hend == NULL) {
+		x = memchr (p, '\n', end - p);
+		if (!x)
+			break;
+		++x;
+		while (isspace (*x)) {
+			/* Found a second line, with only spaces between */
+			if (*x == '\n') {
+				hbeg = data;
+				hend = x;
+				break;
+			/* Found a space between two lines */
+			} else {
+				++x;
+			}
+		}
+
+		/* Try next line */
+		p = x;
+	}
+
+	/* Headers found? */
+	if (hbeg && hend) {
+		data = hend;
+		n_data = end - data;
+	}
+
+	*n_decoded = (n_data * 3) / 4 + 1;
+	if (egg_secure_check (data))
+		*decoded = egg_secure_alloc (*n_decoded);
+	else
+		*decoded = g_malloc0 (*n_decoded);
+	g_return_val_if_fail (*decoded, FALSE);
+
+	*n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
+	if (!*n_decoded) {
+		egg_secure_free (*decoded);
+		return FALSE;
+	}
+
+	if (headers && hbeg && hend)
+		parse_header_lines (hbeg, hend, headers);
+
+	return TRUE;
+}
+
+GHashTable*
+egg_armor_headers_new (void)
+{
+	return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+guint
+egg_armor_parse (gconstpointer data,
+                 gsize n_data,
+                 EggArmorCallback callback,
+                 gpointer user_data)
+{
+	const gchar *beg, *end;
+	const gchar *outer_beg, *outer_end;
+	guint nfound = 0;
+	guchar *decoded = NULL;
+	gsize n_decoded = 0;
+	GHashTable *headers = NULL;
+	GQuark type;
+
+	g_return_val_if_fail (data, 0);
+	g_return_val_if_fail (n_data, 0);
+
+	while (n_data > 0) {
+
+		/* This returns the first character after the PEM BEGIN header */
+		beg = armor_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
+		if (beg == NULL)
+			break;
+
+		g_assert (type);
+
+		/* This returns the character position before the PEM END header */
+		end = armor_find_end ((const gchar*)beg,
+		                      n_data - ((const gchar*)beg - (const gchar *)data),
+		                      type, &outer_end);
+		if (end == NULL)
+			break;
+
+		if (beg != end) {
+			if (armor_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
+				g_assert (outer_end > outer_beg);
+				if (callback != NULL)
+					(callback) (type,
+					            decoded, n_decoded,
+					            outer_beg, outer_end - outer_beg,
+					            headers, user_data);
+				++nfound;
+				egg_secure_free (decoded);
+				if (headers)
+					g_hash_table_remove_all (headers);
+			}
+		}
+
+		/* Try for another block */
+		end += ARMOR_SUFF_L;
+		n_data -= (const gchar*)end - (const gchar*)data;
+		data = end;
+	}
+
+	if (headers)
+		g_hash_table_destroy (headers);
+
+	return nfound;
+}
+
+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);
+	g_string_append_c (string, '\n');
+}
+
+guchar*
+egg_armor_write (const guchar *data,
+                 gsize n_data,
+                 GQuark type,
+                 GHashTable *headers,
+                 gsize *n_result)
+{
+	GString *string;
+	gint state, save;
+	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, ARMOR_PREF_BEGIN, ARMOR_PREF_BEGIN_L);
+	g_string_append (string, g_quark_to_string (type));
+	g_string_append_len (string, ARMOR_SUFF, ARMOR_SUFF_L);
+	g_string_append_c (string, '\n');
+
+	/* The headers */
+	if (headers && g_hash_table_size (headers) > 0) {
+		g_hash_table_foreach (headers, append_each_header, string);
+		g_string_append_c (string, '\n');
+	}
+
+	/* Resize string to fit the base64 data. Algorithm from Glib reference */
+	estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
+	n_prefix = string->len;
+	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, 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_len (string, ARMOR_PREF_END, ARMOR_PREF_END_L);
+	g_string_append (string, g_quark_to_string (type));
+	g_string_append_len (string, ARMOR_SUFF, ARMOR_SUFF_L);
+	g_string_append_c (string, '\n');
+
+	*n_result = string->len;
+	return (guchar*)g_string_free (string, FALSE);
+}
diff --git a/egg/egg-armor.h b/egg/egg-armor.h
new file mode 100644
index 0000000..e0f13d3
--- /dev/null
+++ b/egg/egg-armor.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-armor.h - Armor routines
+
+   Copyright (C) 2007 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#ifndef EGG_ARMOR_H_
+#define EGG_ARMOR_H_
+
+#include <glib.h>
+
+typedef void (*EggArmorCallback) (GQuark type,
+                                  const guchar *data,
+                                  gsize n_data,
+                                  const gchar *outer,
+                                  gsize n_outer,
+                                  GHashTable *headers,
+                                  gpointer user_data);
+
+GHashTable*      egg_armor_headers_new   (void);
+
+guint            egg_armor_parse         (gconstpointer data,
+                                          gsize n_data,
+                                          EggArmorCallback callback,
+                                          gpointer user_data);
+
+guchar*          egg_armor_write         (const guchar *data,
+                                          gsize n_data,
+                                          GQuark type,
+                                          GHashTable *headers,
+                                          gsize *n_result);
+
+#endif /* EGG_ARMOR_H_ */
diff --git a/egg/egg-openssl.c b/egg/egg-openssl.c
index 45daccb..352451f 100644
--- a/egg/egg-openssl.c
+++ b/egg/egg-openssl.c
@@ -35,358 +35,8 @@
 #include <ctype.h>
 #include <string.h>
 
-/* 
- * PEM looks like:
- * 
- * 	-----BEGIN RSA PRIVATE KEY-----
- * 	Proc-Type: 4,ENCRYPTED
- * 	DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
- * 
- * 	4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
- * 	Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
- *	u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
- * 	....
- * 	-----END RSA PRIVATE KEY-----
- */
- 
-#define PEM_SUFF          "-----"
-#define PEM_SUFF_L        5
-#define PEM_PREF_BEGIN    "-----BEGIN "
-#define PEM_PREF_BEGIN_L  11
-#define PEM_PREF_END      "-----END "
-#define PEM_PREF_END_L    9
-
 EGG_SECURE_DECLARE (openssl);
 
-static void
-parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
-{
-	gchar **lines, **l;
-	gchar *line, *name, *value;
-	gchar *copy;
-	
-	copy = g_strndup (hbeg, hend - hbeg);
-	lines = g_strsplit (copy, "\n", 0);
-	g_free (copy);
-	
-	for (l = lines; l && *l; ++l) {
-		line = *l;
-		g_strstrip (line);
-		
-        	/* Look for the break between name: value */
-        	value = strchr (line, ':');
-        	if (value == NULL)
-        		continue;
-        		
-        	*value = 0;
-        	value = g_strdup (value + 1);
-        	g_strstrip (value);
-        	
-        	name = g_strdup (line);
-        	g_strstrip (name);
-        	
-        	if (!*result)
-        		*result = egg_openssl_headers_new ();
-        	g_hash_table_replace (*result, name, value);
-	}
-
-	g_strfreev (lines);
-} 
-
-static const gchar*
-pem_find_begin (const gchar *data,
-                gsize n_data,
-                GQuark *type,
-                const gchar **outer)
-{
-	const gchar *pref, *suff;
-	gchar *stype;
-	
-	/* Look for a prefix */
-	pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
-	if (!pref)
-		return NULL;
-		
-	n_data -= (pref - data) + PEM_PREF_BEGIN_L;
-	data = pref + PEM_PREF_BEGIN_L;
-		
-	/* Look for the end of that begin */
-	suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
-	if (!suff)
-		return NULL;
-		
-	/* Make sure on the same line */
-	if (memchr (pref, '\n', suff - pref))
-		return NULL;
-
-	if (outer)
-		*outer = pref;
-
-	if (type) {
-		*type = 0;
-		pref += PEM_PREF_BEGIN_L;
-		g_assert (suff > pref);
-		stype = g_alloca (suff - pref + 1);
-		memcpy (stype, pref, suff - pref);
-		stype[suff - pref] = 0;
-		*type = g_quark_from_string (stype);
-	}
-
-	/* The byte after this ---BEGIN--- */
-	return suff + PEM_SUFF_L;
-}
-
-static const gchar*
-pem_find_end (const gchar *data,
-              gsize n_data,
-              GQuark type,
-              const gchar **outer)
-{
-	const gchar *stype;
-	const gchar *pref;
-	gsize n_type;
-	
-	/* Look for a prefix */
-	pref = g_strstr_len (data, n_data, PEM_PREF_END);
-	if (!pref)
-		return NULL;
-		
-	n_data -= (pref - data) + PEM_PREF_END_L;
-	data = pref + PEM_PREF_END_L;
-	
-	/* Next comes the type string */
-	stype = g_quark_to_string (type);
-	n_type = strlen (stype);
-	if (strncmp ((gchar*)data, stype, n_type) != 0)
-		return NULL; 
-		
-	n_data -= n_type;
-	data += n_type;
-	
-	/* Next comes the suffix */
-	if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
-		return NULL;
-
-	if (outer != NULL) {
-		data += PEM_SUFF_L;
-		if (isspace (data[0]))
-			data++;
-		*outer = data;
-	}
-
-	/* The beginning of this ---END--- */
-	return pref;
-}
-
-static gboolean
-pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
-                 GHashTable **headers)
-{
-	const gchar *x, *hbeg, *hend;
-	const gchar *p, *end;
-	gint state = 0;
-	guint save = 0;
-	
-	g_assert (data);
-	g_assert (n_data);
-	
-	g_assert (decoded);
-	g_assert (n_decoded);
-	
-	p = data;
-	end = p + n_data;
-	
-	hbeg = hend = NULL;
-	
-	/* Try and find a pair of blank lines with only white space between */
-	while (hend == NULL) {
-		x = memchr (p, '\n', end - p);
-		if (!x)
-			break;
-		++x;
-		while (isspace (*x)) {
-			/* Found a second line, with only spaces between */
-			if (*x == '\n') {
-				hbeg = data;
-				hend = x;
-				break;
-			/* Found a space between two lines */
-			} else {
-				++x;
-			}
-		}
-		
-		/* Try next line */
-		p = x;
-	}
-
-	/* Headers found? */	
-	if (hbeg && hend) {
-		data = hend;
-		n_data = end - data;
-	}
-	
-	*n_decoded = (n_data * 3) / 4 + 1;
-	if (egg_secure_check (data))
-		*decoded = egg_secure_alloc (*n_decoded);
-	else
-		*decoded = g_malloc0 (*n_decoded);
-	g_return_val_if_fail (*decoded, FALSE);
-	
-	*n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
-	if (!*n_decoded) {
-		egg_secure_free (*decoded);
-		return FALSE;
-	}
-	
-	if (headers && hbeg && hend) 
-		parse_header_lines (hbeg, hend, headers);
-	
-	return TRUE;
-}
-
-GHashTable*
-egg_openssl_headers_new (void)
-{
-	return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-}
-
-guint
-egg_openssl_pem_parse (gconstpointer data, gsize n_data,
-                       EggOpensslPemCallback callback, gpointer user_data)
-{
-	const gchar *beg, *end;
-	const gchar *outer_beg, *outer_end;
-	guint nfound = 0;
-	guchar *decoded = NULL;
-	gsize n_decoded = 0;
-	GHashTable *headers = NULL;
-	GQuark type;
-	
-	g_return_val_if_fail (data, 0);
-	g_return_val_if_fail (n_data, 0);
-	g_return_val_if_fail (callback, 0);
-
-	while (n_data > 0) {
-		
-		/* This returns the first character after the PEM BEGIN header */
-		beg = pem_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
-		if (!beg)
-			break;
-			
-		g_assert (type);
-		
-		/* This returns the character position before the PEM END header */
-		end = pem_find_end ((const gchar*)beg, n_data - ((const gchar*)beg - (const gchar *)data),
-		                    type, &outer_end);
-		if (!end)
-			break;
-
-		if (beg != end) {
-			if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
-				g_assert (outer_end > outer_beg);
-				(callback) (type,
-				            decoded, n_decoded,
-				            outer_beg, outer_end - outer_beg,
-				            headers, user_data);
-				++nfound;
-				egg_secure_free (decoded);
-				if (headers)
-					g_hash_table_remove_all (headers);
-			}
-		}
-                     
-		/* Try for another block */
-		end += PEM_SUFF_L;
-		n_data -= (const gchar*)end - (const gchar*)data;
-		data = end;
-	}
-	
-	if (headers)
-		g_hash_table_destroy (headers);
-
-	return nfound;
-}
-
-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);
-	g_string_append_c (string, '\n');
-}
-
-guchar*
-egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type, 
-                    GHashTable *headers, gsize *n_result)
-{
-	GString *string;
-	gint state, save;
-	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));
-	g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
-	g_string_append_c (string, '\n');
-	
-	/* The headers */
-	if (headers && g_hash_table_size (headers) > 0) {
-		g_hash_table_foreach (headers, append_each_header, string);
-		g_string_append_c (string, '\n');
-	}
-
-	/* Resize string to fit the base64 data. Algorithm from Glib reference */
-	estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
-	n_prefix = string->len;
-	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, 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_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);
-}
-
-/* ----------------------------------------------------------------------------
- * DEFINITIONS
- */
-
 static const struct {
 	const gchar *desc;
 	int algo;
diff --git a/egg/egg-openssl.h b/egg/egg-openssl.h
index 49716dd..7d4e4f0 100644
--- a/egg/egg-openssl.h
+++ b/egg/egg-openssl.h
@@ -26,24 +26,6 @@
 
 #include <glib.h>
 
-typedef void (*EggOpensslPemCallback) (GQuark type,
-                                       const guchar *data,
-                                       gsize n_data,
-                                       const gchar *outer,
-                                       gsize n_outer,
-                                       GHashTable *headers,
-                                       gpointer user_data);
-
-GHashTable*      egg_openssl_headers_new       (void);
-
-guint            egg_openssl_pem_parse         (gconstpointer data, gsize n_data,
-                                                EggOpensslPemCallback callback, 
-                                                gpointer user_data);
-
-guchar*          egg_openssl_pem_write         (const guchar *data, gsize n_data, 
-                                                GQuark type, GHashTable *headers,
-                                                gsize *n_result);
-
 int              egg_openssl_parse_algo        (const gchar *name, int *mode);
 
 gboolean         egg_openssl_encrypt_block     (const gchar *dekinfo, const gchar *password, 
diff --git a/egg/tests/test-openssl.c b/egg/tests/test-openssl.c
index e2c3516..da5e457 100644
--- a/egg/tests/test-openssl.c
+++ b/egg/tests/test-openssl.c
@@ -1,5 +1,5 @@
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* unit-test-pkix-openssl.c: Test PKIX openssl
+/* test-openssl.c: Test openssl
 
    Copyright (C) 2008 Stefan Walter
 
@@ -23,6 +23,7 @@
 
 #include "config.h"
 
+#include "egg/egg-armor.h"
 #include "egg/egg-symkey.h"
 #include "egg/egg-openssl.h"
 #include "egg/egg-secure-memory.h"
@@ -93,7 +94,7 @@ parse_reference (GQuark type,
 
 	g_assert ("no headers present in file" && headers != NULL);
 	g_assert (!test->refheaders);
-	test->refheaders = egg_openssl_headers_new ();
+	test->refheaders = egg_armor_headers_new ();
 	g_hash_table_foreach (headers, copy_each_key_value, test->refheaders);
 	dekinfo = egg_openssl_get_dekinfo (headers);
 	g_assert ("no dekinfo in headers" && dekinfo != NULL);
@@ -109,7 +110,7 @@ test_parse_reference (Test *test, gconstpointer unused)
 {
 	guint num;
 
-	num = egg_openssl_pem_parse (test->input, test->n_input, parse_reference, test);
+	num = egg_armor_parse (test->input, test->n_input, parse_reference, test);
 	g_assert ("couldn't PEM block in reference data" && num == 1);
 
 	g_assert ("parse_reference() wasn't called" && test->refdata != NULL);
@@ -124,7 +125,7 @@ test_write_reference (Test *test, gconstpointer unused)
 	gboolean ret;
 	guint num;
 
-	num = egg_openssl_pem_parse (test->input, test->n_input, parse_reference, test);
+	num = egg_armor_parse (test->input, test->n_input, parse_reference, test);
 	g_assert ("couldn't PEM block in reference data" && num == 1);
 
 	dekinfo = egg_openssl_get_dekinfo (test->refheaders);
@@ -146,11 +147,11 @@ test_write_exactly_same (Test *test, gconstpointer unused)
 	gsize n_result;
 	guint num;
 
-	num = egg_openssl_pem_parse (test->input, test->n_input, parse_reference, test);
+	num = egg_armor_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);
+	result = egg_armor_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
@@ -177,7 +178,7 @@ test_openssl_roundtrip (Test *test, gconstpointer unused)
 	int i;
 	guint num;
 
-	num = egg_openssl_pem_parse (test->input, test->n_input, parse_reference, test);
+	num = egg_armor_parse (test->input, test->n_input, parse_reference, test);
 	g_assert ("couldn't PEM block in reference data" && num == 1);
 
 	dekinfo = egg_openssl_prep_dekinfo (test->refheaders);
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index f89100c..38d8856 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -103,6 +103,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-internal.h \
 	gcr-memory.c \
 	gcr-memory-icon.c gcr-memory-icon.h \
+	gcr-openpgp.c gcr-openpgp.h \
 	gcr-openssh.c gcr-openssh.h \
 	gcr-parser.c gcr-parser.h \
 	gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \
diff --git a/gcr/gcr-certificate-exporter.c b/gcr/gcr-certificate-exporter.c
index 115b030..7806533 100644
--- a/gcr/gcr-certificate-exporter.c
+++ b/gcr/gcr-certificate-exporter.c
@@ -24,6 +24,7 @@
 #include "gcr-certificate.h"
 #include "gcr-certificate-exporter.h"
 
+#include "egg/egg-armor.h"
 #include "egg/egg-openssl.h"
 
 #include <glib/gi18n-lib.h>
@@ -96,9 +97,9 @@ prepare_data_for_pem (GcrCertificateExporter *self)
 
 	self->pv->buffer = g_byte_array_new ();
 
-	encoded = egg_openssl_pem_write (data, n_data,
-	                                 g_quark_from_static_string ("CERTIFICATE"),
-	                                 NULL, &n_encoded);
+	encoded = egg_armor_write (data, n_data,
+	                           g_quark_from_static_string ("CERTIFICATE"),
+	                           NULL, &n_encoded);
 
 	g_byte_array_append (self->pv->buffer, encoded, n_encoded);
 	g_free (encoded);
diff --git a/gcr/gcr-openpgp.c b/gcr/gcr-openpgp.c
new file mode 100644
index 0000000..073ec7d
--- /dev/null
+++ b/gcr/gcr-openpgp.c
@@ -0,0 +1,226 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program 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 program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-openpgp.h"
+#include "gcr-internal.h"
+#include "gcr-record.h"
+#include "gcr-types.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <string.h>
+
+static gboolean
+read_byte (const guchar **at,
+           const guchar *end,
+           guchar *result)
+{
+	g_assert (at);
+	if (*at == end)
+		*at = NULL;
+	if (*at == NULL)
+		return FALSE;
+	*result = *((*at)++);
+	return TRUE;
+}
+
+static gboolean
+read_bytes (const guchar **at,
+            const guchar *end,
+            gpointer buffer,
+            gsize length)
+{
+	g_assert (at);
+	if (*at + length >= end)
+		*at = NULL;
+	if (*at == NULL)
+		return FALSE;
+	memcpy (buffer, *at, length);
+	(*at) += length;
+	return TRUE;
+}
+
+static gboolean
+read_uint32 (const guchar **at,
+             const guchar *end,
+             guint32 *value)
+{
+	guchar buf[4];
+	if (!read_bytes (at, end, buf, 4))
+		return FALSE;
+	*value = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+	return TRUE;
+}
+
+static gboolean
+read_uint16 (const guchar **at,
+             const guchar *end,
+             guint16 *value)
+{
+	guchar buf[2];
+	if (!read_bytes (at, end, buf, 2))
+		return FALSE;
+	*value = buf[0] << 8 | buf[1];
+	return TRUE;
+}
+
+static gboolean
+read_new_length (const guchar **at,
+                 const guchar *end,
+                 gsize *pkt_len)
+{
+	guchar c, c1;
+	guint32 val;
+
+	if (!read_byte (at, end, &c))
+		return FALSE;
+	if (c < 192) {
+		*pkt_len = c;
+	} else if (c >= 192 && c <= 223) {
+		if (!read_byte (at, end, &c1))
+			return FALSE;
+		*pkt_len = ((c - 192) << 8) + c1 + 192;
+	} else if (c == 255) {
+		if (!read_uint32 (at, end, &val))
+			return FALSE;
+		*pkt_len = val;
+	} else {
+		/* We don't support partial length */
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+read_old_length (const guchar **at,
+                 const guchar *end,
+                 guchar ctb,
+                 gsize *pkt_len)
+{
+	gsize llen = ctb & 0x03;
+	guint16 v16;
+	guint32 v32;
+	guchar c;
+
+	if (llen == 0) {
+		if (!read_byte (at, end, &c))
+			return FALSE;
+		*pkt_len = c;
+	} else if (llen == 1) {
+		if (!read_uint16 (at, end, &v16))
+			return FALSE;
+		*pkt_len = v16;
+	} else if (llen == 2) {
+		if (!read_uint32 (at, end, &v32))
+			return FALSE;
+		*pkt_len = v32;
+	} else {
+		*pkt_len = end - *at;
+	}
+
+	return TRUE;
+}
+
+static GcrDataError
+read_openpgp_packet (const guchar **at,
+                     const guchar *end,
+                     GPtrArray *records,
+                     gsize *length)
+{
+	guchar pkt_type;
+	gboolean new_ctb;
+	guchar ctb;
+	gboolean ret;
+
+	if (!read_byte (at, end, &ctb))
+		return GCR_ERROR_UNRECOGNIZED;
+	if (!(ctb & 0x80))
+		return GCR_ERROR_UNRECOGNIZED;
+
+	/* RFC2440 packet format. */
+	if (ctb & 0x40) {
+		pkt_type = ctb & 0x3f;
+		new_ctb = TRUE;
+
+	/* the old RFC1991 packet format. */
+	} else {
+		pkt_type = ctb & 0x3f;
+		pkt_type >>= 2;
+		new_ctb = FALSE;
+	}
+
+	if (pkt_type > 63)
+		return GCR_ERROR_UNRECOGNIZED;
+
+	if (new_ctb)
+		ret = read_new_length (at, end, length);
+	else
+		ret = read_old_length (at, end, ctb, length);
+	if (!ret)
+		return GCR_ERROR_UNRECOGNIZED;
+
+	if ((*at) + *length > end)
+		return GCR_ERROR_FAILURE;
+	return GCR_SUCCESS;
+}
+
+guint
+_gcr_openpgp_parse (gconstpointer data,
+                    gsize n_data,
+                    GcrOpenpgpCallback callback,
+                    gpointer user_data)
+{
+	const guchar *at;
+	const guchar *beg;
+	const guchar *end;
+	GPtrArray *records;
+	GcrDataError res;
+	gsize length;
+	guint num_packets = 0;
+
+	g_return_val_if_fail (data != NULL, 0);
+
+	at = data;
+	end = at + n_data;
+
+	while (at != NULL && at != end) {
+		beg = at;
+		records = g_ptr_array_new_with_free_func (_gcr_record_free);
+		res = read_openpgp_packet (&at, end, records, &length);
+		if (res == GCR_SUCCESS && callback != NULL)
+			(callback) (records, beg, (at - beg) + length, user_data);
+
+		g_ptr_array_unref (records);
+
+		if (res != GCR_SUCCESS)
+			break;
+
+		at += length;
+		num_packets++;
+	}
+
+	return num_packets;
+}
diff --git a/gcr/gcr-openpgp.h b/gcr/gcr-openpgp.h
new file mode 100644
index 0000000..3575921
--- /dev/null
+++ b/gcr/gcr-openpgp.h
@@ -0,0 +1,49 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program 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 program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#ifndef __GCR_OPENPGP_H__
+#define __GCR_OPENPGP_H__
+
+#include <glib.h>
+
+#include <gck/gck.h>
+
+G_BEGIN_DECLS
+
+typedef void             (*GcrOpenpgpCallback)             (GPtrArray *records,
+                                                            const guchar *outer,
+                                                            gsize n_outer,
+                                                            gpointer user_data);
+
+guint                    _gcr_openpgp_parse                (gconstpointer data,
+                                                            gsize n_data,
+                                                            GcrOpenpgpCallback callback,
+                                                            gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GCR_OPENPGP_H__ */
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 9e15573..8f8b92f 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -27,10 +27,12 @@
 #include "gcr-importer.h"
 #include "gcr-marshal.h"
 #include "gcr-oids.h"
+#include "gcr-openpgp.h"
 #include "gcr-openssh.h"
 #include "gcr-parser.h"
 #include "gcr-types.h"
 
+#include "egg/egg-armor.h"
 #include "egg/egg-asn1x.h"
 #include "egg/egg-asn1-defs.h"
 #include "egg/egg-dn.h"
@@ -90,6 +92,8 @@
  * @GCR_FORMAT_DER_PKCS8_ENCRYPTED: Encrypted DER encoded PKCS\#8 file which can contain a key
  * @GCR_FORMAT_DER_PKCS12: DER encoded PKCS\#12 file which can contain certificates and/or keys
  * @GCR_FORMAT_OPENSSH_PUBLIC: OpenSSH v1 or v2 public key
+ * @GCR_FORMAT_OPENPGP_PACKET: OpenPGP key packet(s)
+ * @GCR_FORMAT_OPENPGP_ARMOR: OpenPGP public or private key armor encoded data
  * @GCR_FORMAT_PEM: An OpenSSL style PEM file with unspecified contents
  * @GCR_FORMAT_PEM_PRIVATE_KEY_RSA: An OpenSSL style PEM file with a private RSA key
  * @GCR_FORMAT_PEM_PRIVATE_KEY_DSA: An OpenSSL style PEM file with a private DSA key
@@ -186,6 +190,9 @@ static GQuark PEM_PRIVATE_KEY;
 static GQuark PEM_PKCS7;
 static GQuark PEM_PKCS12;
 
+static GQuark ARMOR_PGP_PUBLIC_KEY_BLOCK;
+static GQuark ARMOR_PGP_PRIVATE_KEY_BLOCK;
+
 static void
 init_quarks (void)
 {
@@ -206,7 +213,9 @@ init_quarks (void)
 		QUARK (PEM_ENCRYPTED_PRIVATE_KEY, "ENCRYPTED PRIVATE KEY");
 		QUARK (PEM_PKCS7, "PKCS7");
 		QUARK (PEM_PKCS12, "PKCS12");
-		
+		QUARK (ARMOR_PGP_PRIVATE_KEY_BLOCK, "PGP PRIVATE KEY BLOCK");
+		QUARK (ARMOR_PGP_PUBLIC_KEY_BLOCK, "PGP PUBLIC KEY BLOCK");
+
 		#undef QUARK
 		
 		g_once_init_leave (&quarks_inited, 1);
@@ -1360,6 +1369,40 @@ done:
 	return ret;
 }
 
+
+/* -----------------------------------------------------------------------------
+ * OPENPGP
+ */
+
+static void
+on_openpgp_packet (GPtrArray *records,
+                   const guchar *outer,
+                   gsize n_outer,
+                   gpointer user_data)
+{
+	GcrParser *self = GCR_PARSER (user_data);
+
+	/* All we can do is the packet bounds */
+	parsing_begin (self, outer, n_outer);
+	parsing_object (self, CKO_DATA);
+	parsed_fire (self);
+	parsing_end (self);
+}
+
+static gint
+parse_openpgp_packets (GcrParser *self,
+                       const guchar *data,
+                       gsize n_data)
+{
+	gint num_parsed;
+
+	num_parsed = _gcr_openpgp_parse (data, n_data, on_openpgp_packet, self);
+
+	if (num_parsed == 0)
+		return GCR_ERROR_UNRECOGNIZED;
+	return SUCCESS;
+}
+
 /* -----------------------------------------------------------------------------
  * PEM PARSING 
  */
@@ -1394,7 +1437,13 @@ handle_plain_pem (GcrParser *self, GQuark type, gint subformat,
 		
 	else if (type == PEM_PKCS12)
 		format_id = GCR_FORMAT_DER_PKCS12;
-		
+
+	else if (type == ARMOR_PGP_PRIVATE_KEY_BLOCK)
+		format_id = GCR_FORMAT_OPENPGP_PACKET;
+
+	else if (type == ARMOR_PGP_PUBLIC_KEY_BLOCK)
+		format_id = GCR_FORMAT_OPENPGP_PACKET;
+
 	else
 		return GCR_ERROR_UNRECOGNIZED;
 
@@ -1549,7 +1598,7 @@ handle_pem_format (GcrParser *self, gint subformat, const guchar *data, gsize n_
 	if (n_data == 0)
 		return GCR_ERROR_UNRECOGNIZED;
 	
-	found = egg_openssl_pem_parse (data, n_data, handle_pem_data, &ctx);
+	found = egg_armor_parse (data, n_data, handle_pem_data, &ctx);
 	
 	if (found == 0)
 		return GCR_ERROR_UNRECOGNIZED;
@@ -1579,31 +1628,39 @@ parse_pem_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
 static gint 
 parse_pem_certificate (GcrParser *self, const guchar *data, gsize n_data)
 {
-	return handle_pem_format (self, GCR_FORMAT_PEM_CERTIFICATE_X509, data, n_data);
+	return handle_pem_format (self, GCR_FORMAT_DER_CERTIFICATE_X509, data, n_data);
 }
 
 static gint 
 parse_pem_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
 {
-	return handle_pem_format (self, GCR_FORMAT_PEM_PKCS8_PLAIN, data, n_data);
+	return handle_pem_format (self, GCR_FORMAT_DER_PKCS8_PLAIN, data, n_data);
 }
 
 static gint 
 parse_pem_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
 {
-	return handle_pem_format (self, GCR_FORMAT_PEM_PKCS8_ENCRYPTED, data, n_data);
+	return handle_pem_format (self, GCR_FORMAT_DER_PKCS8_ENCRYPTED, data, n_data);
 }
 
 static gint 
 parse_pem_pkcs7 (GcrParser *self, const guchar *data, gsize n_data)
 {
-	return handle_pem_format (self, GCR_FORMAT_PEM_PKCS7, data, n_data);
+	return handle_pem_format (self, GCR_FORMAT_DER_PKCS7, data, n_data);
 }
 
 static gint 
 parse_pem_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
 {
-	return handle_pem_format (self, GCR_FORMAT_PEM_PKCS12, data, n_data);
+	return handle_pem_format (self, GCR_FORMAT_DER_PKCS12, data, n_data);
+}
+
+static gint
+parse_openpgp_armor (GcrParser *self,
+                     const guchar *data,
+                     gsize n_data)
+{
+	return handle_pem_format (self, GCR_FORMAT_OPENPGP_PACKET, data, n_data);
 }
 
 /* -----------------------------------------------------------------------------
@@ -1628,9 +1685,9 @@ on_openssh_public_key_parsed (GckAttributes *attrs,
 }
 
 static gint
-parse_der_openssh_public (GcrParser *self,
-                          const guchar *data,
-                          gsize n_data)
+parse_openssh_public (GcrParser *self,
+                      const guchar *data,
+                      gsize n_data)
 {
 	guint num_parsed;
 
@@ -1656,7 +1713,9 @@ static const ParserFormat parser_normal[] = {
 	{ GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
 	{ GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
 	{ GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 },
-	{ GCR_FORMAT_OPENSSH_PUBLIC, parse_der_openssh_public },
+	{ GCR_FORMAT_OPENSSH_PUBLIC, parse_openssh_public },
+	{ GCR_FORMAT_OPENPGP_PACKET, parse_openpgp_packets },
+	{ GCR_FORMAT_OPENPGP_ARMOR, parse_openpgp_armor },
 };
 
 /* Must be in format_id numeric order */
@@ -1670,7 +1729,9 @@ static const ParserFormat parser_formats[] = {
 	{ GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
 	{ GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
 	{ GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 },
-	{ GCR_FORMAT_OPENSSH_PUBLIC, parse_der_openssh_public },
+	{ GCR_FORMAT_OPENSSH_PUBLIC, parse_openssh_public },
+	{ GCR_FORMAT_OPENPGP_PACKET, parse_openpgp_packets },
+	{ GCR_FORMAT_OPENPGP_ARMOR, parse_openpgp_armor },
 	{ GCR_FORMAT_PEM, parse_pem },
 	{ GCR_FORMAT_PEM_PRIVATE_KEY_RSA, parse_pem_private_key_rsa },
 	{ GCR_FORMAT_PEM_PRIVATE_KEY_DSA, parse_pem_private_key_dsa },
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
index 184eedd..317e552 100644
--- a/gcr/gcr-types.h
+++ b/gcr/gcr-types.h
@@ -73,6 +73,9 @@ typedef enum {
 
 	GCR_FORMAT_OPENSSH_PUBLIC = 600,
 
+	GCR_FORMAT_OPENPGP_PACKET = 700,
+	GCR_FORMAT_OPENPGP_ARMOR,
+
 	GCR_FORMAT_PEM = 1000,
 	GCR_FORMAT_PEM_PRIVATE_KEY_RSA,
 	GCR_FORMAT_PEM_PRIVATE_KEY_DSA,
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index a63b090..aef09b0 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -26,6 +26,7 @@ TEST_PROGS = \
 	test-certificate-chain \
 	test-fingerprint \
 	test-pkcs11-certificate \
+	test-openpgp \
 	test-openssh \
 	test-trust \
 	test-parser \
diff --git a/gcr/tests/files/pubring.gpg b/gcr/tests/files/pubring.gpg
new file mode 100644
index 0000000..10b1372
Binary files /dev/null and b/gcr/tests/files/pubring.gpg differ
diff --git a/gcr/tests/files/werner-koch.asc b/gcr/tests/files/werner-koch.asc
new file mode 100644
index 0000000..eb70c97
--- /dev/null
+++ b/gcr/tests/files/werner-koch.asc
@@ -0,0 +1,71 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+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
+/Yn6BAmPF+6YxQo2W98rzq0THS5wKJjXmHw=
+=F0ww
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/gcr/tests/test-openpgp.c b/gcr/tests/test-openpgp.c
new file mode 100644
index 0000000..7d4fd5e
--- /dev/null
+++ b/gcr/tests/test-openpgp.c
@@ -0,0 +1,94 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+   Copyright (C) 2011 Collabora Ltd
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "gcr/gcr.h"
+#include "gcr/gcr-openpgp.h"
+
+#include "egg/egg-armor.h"
+#include "egg/egg-testing.h"
+
+#include <gcrypt.h>
+#include <glib.h>
+#include <string.h>
+
+static void
+on_openpgp_packet  (GPtrArray *records,
+                    const guchar *outer,
+                    gsize n_outer,
+                    gpointer user_data)
+{
+	guint num_packets;
+
+	/* Should be parseable again */
+	num_packets = _gcr_openpgp_parse (outer, n_outer, NULL, NULL);
+	g_assert_cmpuint (num_packets, ==, 1);
+}
+
+static void
+on_armor_parsed (GQuark type,
+                 const guchar *data,
+                 gsize n_data,
+                 const gchar *outer,
+                 gsize n_outer,
+                 GHashTable *headers,
+                 gpointer user_data)
+{
+	const gchar *value;
+	guint num_packets;
+
+	value = g_hash_table_lookup (headers, "Version");
+	g_assert_cmpstr (value, ==, "GnuPG v1.4.11 (GNU/Linux)");
+
+	num_packets = _gcr_openpgp_parse (data, n_data, on_openpgp_packet, NULL);
+	g_assert_cmpuint (num_packets, ==, 21);
+}
+
+static void
+test_armor_parse (void)
+{
+	GError *error = NULL;
+	gchar *armor;
+	gsize length;
+	guint parts;
+
+	g_file_get_contents (SRCDIR "/files/werner-koch.asc", &armor, &length, &error);
+	g_assert_no_error (error);
+
+	parts = egg_armor_parse (armor, length, on_armor_parsed, NULL);
+	g_assert_cmpuint (parts, ==, 1);
+
+	g_free (armor);
+}
+
+int
+main (int argc, char **argv)
+{
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+	g_set_prgname ("test-gnupg-process");
+
+	g_test_add_func ("/gcr/openpgp/armor_parse", test_armor_parse);
+
+	return g_test_run ();
+}
diff --git a/pkcs11/gkm/tests/test-data-der.c b/pkcs11/gkm/tests/test-data-der.c
index 6b5531d..2bc90df 100644
--- a/pkcs11/gkm/tests/test-data-der.c
+++ b/pkcs11/gkm/tests/test-data-der.c
@@ -28,6 +28,7 @@
 #include "gkm/gkm-data-der.h"
 #include "gkm/gkm-sexp.h"
 
+#include "egg/egg-armor.h"
 #include "egg/egg-asn1x.h"
 #include "egg/egg-asn1-defs.h"
 #include "egg/egg-openssl.h"
@@ -338,7 +339,7 @@ test_read_ca_certificates_public_key_info (Test *test, gconstpointer unused)
 
 	if (!g_file_get_contents (SRCDIR "/files/ca-certificates.crt", &data, &n_data, NULL))
 		g_assert_not_reached ();
-	egg_openssl_pem_parse (data, n_data, on_ca_certificate_public_key_info, NULL);
+	egg_armor_parse (data, n_data, on_ca_certificate_public_key_info, NULL);
 	g_free (data);
 }
 
diff --git a/pkcs11/roots-store/gkm-roots-module.c b/pkcs11/roots-store/gkm-roots-module.c
index 32569f4..e337623 100644
--- a/pkcs11/roots-store/gkm-roots-module.c
+++ b/pkcs11/roots-store/gkm-roots-module.c
@@ -28,6 +28,7 @@
 #include "gkm/gkm-file-tracker.h"
 #include "gkm/gkm-serializable.h"
 
+#include "egg/egg-armor.h"
 #include "egg/egg-error.h"
 #include "egg/egg-openssl.h"
 
@@ -210,7 +211,7 @@ file_load (GkmFileTracker *tracker, const gchar *path, GkmRootsModule *self)
 	g_list_free (objects);
 
 	/* Try and parse the PEM */
-	egg_openssl_pem_parse (data, n_data, parsed_pem_block, &ctx);
+	egg_armor_parse (data, n_data, parsed_pem_block, &ctx);
 
 	/* If no PEM data, try to parse directly as DER  */
 	if (ctx.count == 0) {
diff --git a/pkcs11/ssh-store/gkm-ssh-openssh.c b/pkcs11/ssh-store/gkm-ssh-openssh.c
index a20ce92..5003471 100644
--- a/pkcs11/ssh-store/gkm-ssh-openssh.c
+++ b/pkcs11/ssh-store/gkm-ssh-openssh.c
@@ -5,6 +5,7 @@
 #include "gkm/gkm-data-der.h"
 #include "gkm/gkm-data-types.h"
 
+#include "egg/egg-armor.h"
 #include "egg/egg-asn1x.h"
 #include "egg/egg-buffer.h"
 #include "egg/egg-openssl.h"
@@ -385,7 +386,7 @@ gkm_ssh_openssh_parse_private_key (gconstpointer data, gsize n_data,
 	ctx.password = password;
 	ctx.n_password = n_password;
 
-	num = egg_openssl_pem_parse (data, n_data, parsed_pem_block, &ctx);
+	num = egg_armor_parse (data, n_data, parsed_pem_block, &ctx);
 
 	/* Didn't find any private key there */
 	if (num == 0 || !ctx.seen) {
@@ -401,6 +402,6 @@ gchar*
 gkm_ssh_openssh_digest_private_key (const guchar *data, gsize n_data)
 {
 	gchar *result = NULL;
-	egg_openssl_pem_parse (data, n_data, digest_pem_block, &result);
+	egg_armor_parse (data, n_data, digest_pem_block, &result);
 	return result;
 }



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