gcr: Implement recognizing of OpenPGP packets
authorStef Walter <stefw@collabora.co.uk>
Tue, 13 Sep 2011 15:07:28 +0000 (17:07 +0200)
committerStef Walter <stefw@collabora.co.uk>
Tue, 27 Sep 2011 07:32:40 +0000 (09:32 +0200)
 * Doesn't actually parse keys or anything else about OpenPGP (yet).
 * Generalize the PEM code so that it works for PGP armor.

17 files changed:
docs/reference/gcr/gcr-sections.txt
egg/Makefile.am
egg/egg-armor.c [new file with mode: 0644]
egg/egg-armor.h [new file with mode: 0644]
egg/egg-openssl.c
egg/egg-openssl.h
egg/tests/test-openssl.c
gcr/Makefile.am
gcr/gcr-certificate-exporter.c
gcr/gcr-openpgp.c [new file with mode: 0644]
gcr/gcr-openpgp.h [new file with mode: 0644]
gcr/gcr-parser.c
gcr/gcr-types.h
gcr/tests/Makefile.am
gcr/tests/files/pubring.gpg [new file with mode: 0644]
gcr/tests/files/werner-koch.asc [new file with mode: 0644]
gcr/tests/test-openpgp.c [new file with mode: 0644]

index dfb22ab..ea40ee1 100644 (file)
@@ -590,4 +590,5 @@ GcrMemoryIconPrivate
 GCR_ERROR
 gcr_error_get_domain
 GcrOpensshPubCallback
+GcrOpenpgpCallback
 </SECTION>
index d27c811..4b518e9 100644 (file)
@@ -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 (file)
index 0000000..5a4b047
--- /dev/null
@@ -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 (file)
index 0000000..e0f13d3
--- /dev/null
@@ -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_ */
index 1b54658..77318f5 100644 (file)
 #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;
index 5914fdb..a998c9e 100644 (file)
 
 #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,
index e2c3516..da5e457 100644 (file)
@@ -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);
index 78a5448..8dfeef2 100644 (file)
@@ -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 \
index 115b030..7806533 100644 (file)
@@ -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 (file)
index 0000000..073ec7d
--- /dev/null
@@ -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 (file)
index 0000000..3575921
--- /dev/null
@@ -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__ */
index 7d61483..fb6c37c 100644 (file)
 #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,6 +213,8 @@ 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
 
@@ -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
  */
@@ -1395,6 +1438,12 @@ 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 },
index 184eedd..317e552 100644 (file)
@@ -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,
index a63b090..aef09b0 100644 (file)
@@ -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 (file)
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 (file)
index 0000000..eb70c97
--- /dev/null
@@ -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 (file)
index 0000000..7d4fd5e
--- /dev/null
@@ -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 ();
+}