[gnome-keyring/dbus-api] Implement prompt password return via encryption.



commit 140996d802b7ad338a3377dd6c368169c2c5e056
Author: Stef Walter <stef memberwebs com>
Date:   Tue Nov 3 03:06:44 2009 +0000

    Implement prompt password return via encryption.
    
    * Prompt password encrypted return via DH key negotiation.
     * Factor out some common code between prompt-tool and daemon.
     * Add tests for common code.

 configure.in                                       |    1 +
 daemon/.gitignore                                  |    1 +
 daemon/prompt/Makefile.am                          |   16 ++-
 daemon/prompt/gkd-prompt-tool.c                    |  116 +++++++++-
 daemon/prompt/gkd-prompt-util.c                    |  248 ++++++++++++++++++++
 daemon/prompt/gkd-prompt-util.h                    |   49 ++++
 daemon/prompt/gkd-prompt.c                         |  160 ++------------
 daemon/prompt/tests/Makefile.am                    |   14 ++
 .../prompt/{test => tests}/test-data/prompt-empty  |    0
 .../prompt/{test => tests}/test-data/prompt-full   |    0
 .../prompt/{test => tests}/test-data/prompt-test   |    0
 daemon/prompt/tests/unit-test-util.c               |  146 ++++++++++++
 egg/Makefile.am                                    |   20 ++-
 egg/egg-dh.c                                       |    4 +
 egg/egg-dh.h                                       |    2 +
 15 files changed, 621 insertions(+), 156 deletions(-)
---
diff --git a/configure.in b/configure.in
index bacc953..0c97846 100644
--- a/configure.in
+++ b/configure.in
@@ -533,6 +533,7 @@ daemon/keyrings/Makefile
 daemon/keyrings/tests/Makefile
 daemon/pkcs11/Makefile
 daemon/prompt/Makefile
+daemon/prompt/tests/Makefile
 daemon/ui/Makefile
 daemon/util/Makefile
 daemon/util/tests/Makefile
diff --git a/daemon/.gitignore b/daemon/.gitignore
index 977c5e9..f6d0066 100644
--- a/daemon/.gitignore
+++ b/daemon/.gitignore
@@ -9,4 +9,5 @@ Makefile.in
 /gnome-keyring-daemon.desktop.in
 *-marshal.[ch]
 gnome-keyring-prompt
+run-auto-test*
 
diff --git a/daemon/prompt/Makefile.am b/daemon/prompt/Makefile.am
index a1a2379..2269fd0 100644
--- a/daemon/prompt/Makefile.am
+++ b/daemon/prompt/Makefile.am
@@ -1,4 +1,12 @@
 
+if WITH_TESTS
+TESTS_DIR = tests
+else
+TESTS_DIR =
+endif
+
+SUBDIRS = . $(TESTS_DIR)
+
 INCLUDES= \
 	-DPREFIX=\""$(prefix)"\" \
 	-DBINDIR=\""$(bindir)"\" \
@@ -28,6 +36,7 @@ BUILT_SOURCES = \
 
 libgkd_prompt_la_SOURCES = \
 	gkd-prompt.c gkd-prompt.h \
+	gkd-prompt-util.c gkd-prompt-util.h \
 	$(BUILT_SOURCES)
 
 libgkd_prompt_la_LIBADD = \
@@ -51,12 +60,15 @@ libexec_PROGRAMS= \
 	gnome-keyring-prompt
 
 gnome_keyring_prompt_SOURCES = \
-	gkd-prompt-tool.c
+	gkd-prompt-tool.c \
+	gkd-prompt-util.c gkd-prompt-util.h
 
 gnome_keyring_prompt_LDADD = \
-	$(top_builddir)/egg/libegg.la \
+	$(top_builddir)/egg/libegg-prompt.la \
+	$(LIBGCRYPT_LIBS) \
 	$(GTK_LIBS)
 
 gnome_keyring_prompt_CFLAGS = \
 	-DUIDIR=\""$(uidir)"\" \
+	$(LIBGCRYPT_CFLAGS) \
 	$(GTK_CFLAGS)
diff --git a/daemon/prompt/gkd-prompt-tool.c b/daemon/prompt/gkd-prompt-tool.c
index d1d0b7e..39ee2bb 100644
--- a/daemon/prompt/gkd-prompt-tool.c
+++ b/daemon/prompt/gkd-prompt-tool.c
@@ -22,23 +22,33 @@
 
 #include "config.h"
 
+#include "gkd-prompt-util.h"
+
+#include "egg/egg-dh.h"
 #include "egg/egg-secure-memory.h"
 
-#include <gtk/gtk.h>
+#include <gcrypt.h>
+
 #include <glib/gi18n.h>
 
-#include <stdio.h>
-#include <string.h>
+#include <gtk/gtk.h>
+
+#include <errno.h>
 #include <locale.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
+#include <string.h>
 #include <syslog.h>
+#include <unistd.h>
 
 static GKeyFile *input_data = NULL;
 static GKeyFile *output_data = NULL;
 static gboolean keyboard_grabbed = FALSE;
 
+/* An encryption key for returning passwords */
+static gpointer the_key = NULL;
+static gsize n_the_key = 0;
+
 #define LOG_ERRORS 1
 
 /* ------------------------------------------------------------------------------ */
@@ -212,11 +222,91 @@ prepare_dialog (GtkBuilder *builder)
 	return dialog;
 }
 
+static gboolean
+negotiate_transport_crypto (void)
+{
+	gcry_mpi_t base, prime, peer;
+	gcry_mpi_t key, pub, secret;
+	gboolean ret = FALSE;
+
+	g_assert (!the_key);
+	base = prime = peer = NULL;
+	key = pub = secret = NULL;
+
+	/* The DH stuff coming in from our caller */
+	if (gkd_prompt_util_decode_mpi (input_data, "transport", "prime", &prime) &&
+	    gkd_prompt_util_decode_mpi (input_data, "transport", "base", &base) &&
+	    gkd_prompt_util_decode_mpi (input_data, "transport", "public", &peer)) {
+
+		/* Generate our own public/secret, and then a key, send it back */
+		if (egg_dh_gen_secret (prime, base, &pub, &secret) &&
+		    egg_dh_gen_key (peer, secret, prime, &key)) {
+
+			/* Build up a key we can use */
+			gkd_prompt_util_encode_mpi (output_data, "transport", "public", pub);
+			if (gkd_prompt_util_mpi_to_key (key, &the_key, &n_the_key))
+				ret = TRUE;
+		}
+	}
+
+	gcry_mpi_release (base);
+	gcry_mpi_release (prime);
+	gcry_mpi_release (peer);
+	gcry_mpi_release (key);
+	gcry_mpi_release (pub);
+	gcry_mpi_release (secret);
+
+	return ret;
+}
+
+static void
+gather_password (GtkBuilder *builder, const gchar *password_type)
+{
+	GtkEntry *entry;
+	gchar iv[16];
+	gpointer data;
+	gsize n_data;
+
+	entry = GTK_ENTRY (gtk_builder_get_object (builder, "password_entry"));
+	g_return_if_fail (GTK_IS_ENTRY (entry));
+
+	/* A non-encrypted password: just send the value back */
+	if (!g_key_file_has_group (input_data, "transport")) {
+		g_key_file_set_boolean (output_data, password_type, "encrypted", FALSE);
+		g_key_file_set_value (output_data, password_type, "value",
+		                      gtk_entry_get_text (entry));
+		return;
+	}
+
+	g_key_file_set_boolean (output_data, password_type, "encrypted", TRUE);
+	if (!the_key && !negotiate_transport_crypto ()) {
+		g_warning ("couldn't negotiate transport crypto for password");
+		return;
+	}
+
+	gcry_create_nonce (iv, sizeof (iv));
+	data = gkd_prompt_util_encrypt_text (the_key, n_the_key, iv, sizeof (iv),
+	                                     gtk_entry_get_text (entry), &n_data);
+	g_return_if_fail (data);
+
+	gkd_prompt_util_encode_hex (output_data, password_type, "iv", iv, sizeof (iv));
+	gkd_prompt_util_encode_hex (output_data, password_type, "value", data, n_data);
+
+	g_free (data);
+}
+
+static void
+gather_dialog (GtkBuilder *builder, GtkDialog *dialog)
+{
+	gather_password (builder, "password");
+}
+
 static void
 run_dialog (void)
 {
 	GtkBuilder *builder;
 	GtkDialog *dialog;
+	gint res;
 
 	builder = gtk_builder_new ();
 	dialog = prepare_dialog (builder);
@@ -227,11 +317,13 @@ run_dialog (void)
 
 	for (;;) {
 		gtk_widget_show (GTK_WIDGET (dialog));
-		switch (gtk_dialog_run (dialog)) {
+		res = gtk_dialog_run (dialog);
+		switch (res) {
 		case GTK_RESPONSE_OK:
-			/* TODO: if (!validate_dialog (builder, dialog, response))
+		case GTK_RESPONSE_APPLY:
+			/* if (!validate_dialog (builder, dialog, res))
 				continue; */
-			/* TODO: output */
+			gather_dialog (builder, dialog);
 			break;
 		case GTK_RESPONSE_CANCEL:
 		case GTK_RESPONSE_DELETE_EVENT:
@@ -477,6 +569,14 @@ main (int argc, char *argv[])
 
 	run_dialog ();
 
+	/* Cleanup after any key */
+	if (the_key) {
+		egg_secure_clear (the_key, n_the_key);
+		egg_secure_free (the_key);
+		the_key = NULL;
+		n_the_key = 0;
+	}
+
 	g_key_file_free (input_data);
 	data = g_key_file_to_data (output_data, &length, &err);
 	g_key_file_free (output_data);
diff --git a/daemon/prompt/gkd-prompt-util.c b/daemon/prompt/gkd-prompt-util.c
new file mode 100644
index 0000000..151940a
--- /dev/null
+++ b/daemon/prompt/gkd-prompt-util.c
@@ -0,0 +1,248 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gkd-prompt-tool.c - Handles gui authentication for the keyring daemon.
+
+   Copyright (C) 2009 Stefan Walter
+
+   Gnome keyring is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   Gnome keyring 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gkd-prompt-util.h"
+
+#include "egg/egg-dh.h"
+#include "egg/egg-hex.h"
+#include "egg/egg-secure-memory.h"
+
+void
+gkd_prompt_util_encode_mpi (GKeyFile *key_file, const gchar *section,
+                            const gchar *field, gcry_mpi_t mpi)
+{
+	gcry_error_t gcry;
+	guchar *data;
+	gsize n_data;
+
+	g_return_if_fail (key_file);
+	g_return_if_fail (section);
+	g_return_if_fail (field);
+	g_return_if_fail (mpi);
+
+	/* Get the size */
+	gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, NULL, 0, &n_data, mpi);
+	g_return_if_fail (gcry == 0);
+
+	data = g_malloc0 (n_data + 1);
+
+	/* Write into buffer */
+	gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, data, n_data, &n_data, mpi);
+	g_return_if_fail (gcry == 0);
+
+	g_key_file_set_value (key_file, section, field, (gchar*)data);
+	g_free (data);
+}
+
+void
+gkd_prompt_util_encode_hex (GKeyFile *key_file, const gchar *section,
+                            const gchar *field, gconstpointer data, gsize n_data)
+{
+	gchar *value;
+
+	g_return_if_fail (key_file);
+	g_return_if_fail (section);
+	g_return_if_fail (field);
+
+	value = egg_hex_encode (data, n_data);
+	g_key_file_set_value (key_file, section, field, value);
+	g_free (value);
+}
+
+gpointer
+gkd_prompt_util_decode_hex (GKeyFile *key_file, const gchar *section,
+                            const gchar *field, gsize *n_result)
+{
+	gpointer result = NULL;
+	gchar *data;
+
+	g_return_val_if_fail (key_file, NULL);
+	g_return_val_if_fail (section, NULL);
+	g_return_val_if_fail (field, NULL);
+	g_return_val_if_fail (n_result, NULL);
+
+	data = g_key_file_get_value (key_file, section, field, NULL);
+	if (data != NULL)
+		result = egg_hex_decode (data, -1, n_result);
+	g_free (data);
+	return result;
+}
+
+gboolean
+gkd_prompt_util_decode_mpi (GKeyFile *key_file, const gchar *section,
+                            const gchar *field, gcry_mpi_t *mpi)
+{
+	gcry_error_t gcry;
+	gchar *data;
+
+	g_return_val_if_fail (key_file, FALSE);
+	g_return_val_if_fail (section, FALSE);
+	g_return_val_if_fail (field, FALSE);
+	g_return_val_if_fail (mpi, FALSE);
+
+	data = g_key_file_get_value (key_file, section, field, NULL);
+	if (data == NULL)
+		return FALSE;
+
+	gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_HEX, data, 0, NULL);
+	g_free (data);
+
+	return (gcry == 0);
+}
+
+gboolean
+gkd_prompt_util_mpi_to_key (gcry_mpi_t mpi, gpointer *key, gsize *n_key)
+{
+	gcry_error_t gcry;
+	guchar *buffer;
+	gsize n_buffer;
+
+	g_return_val_if_fail (mpi, FALSE);
+	g_return_val_if_fail (key, FALSE);
+	g_return_val_if_fail (n_key, FALSE);
+
+	/* Write the key out to raw data */
+	gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_buffer, mpi);
+	g_return_val_if_fail (gcry == 0, FALSE);
+	buffer = egg_secure_alloc (n_buffer);
+	gcry = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, n_buffer, &n_buffer, mpi);
+	g_return_val_if_fail (gcry == 0, FALSE);
+
+	/* Allocate memory for hashed key */
+	g_assert (16 == gcry_md_get_algo_dlen (GCRY_MD_MD5));
+	*key = egg_secure_alloc (16);
+	*n_key = 16;
+
+	/* Use that as the input to derive a key for 128-bit AES */
+	gcry_md_hash_buffer (GCRY_MD_MD5, *key, buffer, n_buffer);
+
+	egg_secure_free (buffer);
+	return TRUE;
+}
+
+gpointer
+gkd_prompt_util_encrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
+                              const gchar *text, gsize *n_result)
+{
+	gcry_cipher_hd_t cih;
+	gcry_error_t gcry;
+	guchar* padded;
+	guchar* result;
+	gsize n_text;
+	gsize pos;
+
+	g_return_val_if_fail (key, NULL);
+	g_return_val_if_fail (n_key == 16, NULL);
+	g_return_val_if_fail (iv, NULL);
+	g_return_val_if_fail (n_iv == 16, NULL);
+
+	gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
+	if (gcry) {
+		g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
+		return NULL;
+	}
+
+	/* 16 = 128 bits */
+	gcry = gcry_cipher_setkey (cih, key, 16);
+	g_return_val_if_fail (gcry == 0, NULL);
+
+	/* 16 = 128 bits */
+	gcry = gcry_cipher_setiv (cih, iv, 16);
+	g_return_val_if_fail (gcry == 0, NULL);
+
+	/* Allocate memory for the operation */
+	n_text = strlen (text) + 1;
+	*n_result = ((n_text + 15) / 16) * 16;
+	padded = egg_secure_alloc (*n_result);
+	result = g_malloc0 (*n_result);
+
+	/* Setup the padding */
+	memset (padded, 0, *n_result);
+	memcpy (padded, text, n_text);
+
+	for (pos = 0; pos < *n_result; pos += 16) {
+		gcry = gcry_cipher_encrypt (cih, result + pos, 16, padded + pos, 16);
+		g_return_val_if_fail (gcry == 0, NULL);
+	}
+
+	gcry_cipher_close (cih);
+
+	egg_secure_clear (padded, *n_result);
+	egg_secure_free (padded);
+	return result;
+}
+
+gchar*
+gkd_prompt_util_decrypt_text (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
+                              gpointer data, gsize n_data)
+{
+	gcry_cipher_hd_t cih;
+	gcry_error_t gcry;
+	gchar *result;
+	gsize pos;
+
+	g_return_val_if_fail (key, NULL);
+	g_return_val_if_fail (n_key == 16, NULL);
+
+	if (n_iv != 16) {
+		g_warning ("prompt response has iv of wrong length");
+		return NULL;
+	}
+
+	if (n_data % 16 != 0) {
+		g_warning ("prompt response encrypted password of wrong length");
+		return NULL;
+	}
+
+	gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
+	if (gcry) {
+		g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
+		return NULL;
+	}
+
+	/* 16 = 128 bits */
+	gcry = gcry_cipher_setkey (cih, key, 16);
+	g_return_val_if_fail (gcry == 0, NULL);
+
+	/* 16 = 128 bits */
+	gcry = gcry_cipher_setiv (cih, iv, 16);
+	g_return_val_if_fail (gcry == 0, NULL);
+
+	/* Allocate memory for the result */
+	result = egg_secure_alloc (n_data);
+
+	for (pos = 0; pos < n_data; pos += 16) {
+		gcry = gcry_cipher_decrypt (cih, result + pos, 16, (guchar*)data + pos, 16);
+		g_return_val_if_fail (gcry == 0, NULL);
+	}
+
+	gcry_cipher_close (cih);
+
+	if (!g_utf8_validate (result, -1, NULL)) {
+		egg_secure_free (result);
+		return NULL;
+	}
+
+	return result;
+}
diff --git a/daemon/prompt/gkd-prompt-util.h b/daemon/prompt/gkd-prompt-util.h
new file mode 100644
index 0000000..50a8c28
--- /dev/null
+++ b/daemon/prompt/gkd-prompt-util.h
@@ -0,0 +1,49 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * 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.
+ */
+
+#ifndef __GKD_PROMPT_UTIL_H__
+#define __GKD_PROMPT_UTIL_H__
+
+#include <gcrypt.h>
+
+#include <glib.h>
+
+void        gkd_prompt_util_encode_mpi                   (GKeyFile *key_file, const gchar *section,
+                                                          const gchar *field, gcry_mpi_t mpi);
+
+void        gkd_prompt_util_encode_hex                   (GKeyFile *key_file, const gchar *section,
+                                                          const gchar *field, gconstpointer data, gsize n_data);
+
+gboolean    gkd_prompt_util_decode_mpi                   (GKeyFile *key_file, const gchar *section,
+                                                          const gchar *field, gcry_mpi_t *mpi);
+
+gpointer    gkd_prompt_util_decode_hex                   (GKeyFile *key_file, const gchar *section,
+                                                          const gchar *field, gsize *n_result);
+
+gpointer    gkd_prompt_util_encrypt_text                 (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
+                                                          const gchar *text, gsize *n_result);
+
+gchar*      gkd_prompt_util_decrypt_text                 (gpointer key, gsize n_key, gpointer iv, gsize n_iv,
+                                                          gpointer data, gsize n_data);
+
+gboolean    gkd_prompt_util_mpi_to_key                   (gcry_mpi_t mpi, gpointer *key, gsize *n_key);
+
+#endif /* __GKD_PROMPT_H__ */
diff --git a/daemon/prompt/gkd-prompt.c b/daemon/prompt/gkd-prompt.c
index 199fe51..1777e85 100644
--- a/daemon/prompt/gkd-prompt.c
+++ b/daemon/prompt/gkd-prompt.c
@@ -23,6 +23,7 @@
 
 #include "gkd-prompt.h"
 #include "gkd-prompt-marshal.h"
+#include "gkd-prompt-util.h"
 
 #include "egg/egg-cleanup.h"
 #include "egg/egg-dh.h"
@@ -52,7 +53,7 @@ struct _GkdPromptPrivate {
 	/* Transport crypto */
 	gcry_mpi_t secret;
 	gcry_mpi_t prime;
-	guchar *key;
+	gpointer key;
 	gsize n_key;
 
 	/* Information about child */
@@ -234,32 +235,6 @@ on_child_exited (GPid pid, gint status, gpointer user_data)
 	g_spawn_close_pid (pid);
 }
 
-static gboolean
-encode_input_mpi (GkdPrompt *self, const gchar *section,
-                  const gchar *field, gcry_mpi_t mpi)
-{
-	gcry_error_t gcry;
-	guchar *data;
-	gsize n_data;
-
-	g_assert (self->pv->input);
-
-	/* Get the size */
-	gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, NULL, 0, &n_data, mpi);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	data = g_malloc0 (n_data + 1);
-
-	/* Write into buffer */
-	gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, data, n_data, &n_data, mpi);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	g_key_file_set_value (self->pv->input, section, field, (gchar*)data);
-	g_free (data);
-
-	return TRUE;
-}
-
 static void
 prepare_transport_crypto (GkdPrompt *self)
 {
@@ -274,64 +249,23 @@ prepare_transport_crypto (GkdPrompt *self)
 		g_return_if_reached ();
 
 	/* Send over the prime, base, and public bits */
-	if (!encode_input_mpi (self, "transport", "prime", self->pv->prime) ||
-	    !encode_input_mpi (self, "transport", "base", base) ||
-	    !encode_input_mpi (self, "transport", "public", pub))
-		g_return_if_reached ();
+	gkd_prompt_util_encode_mpi (self->pv->input, "transport", "prime", self->pv->prime);
+	gkd_prompt_util_encode_mpi (self->pv->input, "transport", "base", base);
+	gkd_prompt_util_encode_mpi (self->pv->input, "transport", "public", pub);
 
 	gcry_mpi_release (base);
 	gcry_mpi_release (pub);
 }
 
 static gboolean
-decode_output_mpi (GkdPrompt *self, const gchar *section,
-                   const gchar *field, gcry_mpi_t *mpi)
-{
-	gcry_error_t gcry;
-	gchar *data;
-
-	g_assert (self->pv->output);
-
-	data = g_key_file_get_value (self->pv->output, section, field, NULL);
-	if (!data)
-		return FALSE;
-
-	gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_HEX, data, 0, NULL);
-	g_free (data);
-
-	return (gcry == 0);
-}
-
-static guchar*
-decode_output_hex (GkdPrompt *self, const gchar *section,
-                   const gchar *field, gsize *n_result)
-{
-	guchar *result;
-	gchar *data;
-
-	g_assert (self->pv->output);
-
-	data = g_key_file_get_value (self->pv->output, section, field, NULL);
-	if (!data)
-		return NULL;
-
-	result = egg_hex_decode (data, -1, n_result);
-	g_free (data);
-	return result;
-}
-
-static gboolean
 receive_transport_crypto (GkdPrompt *self)
 {
 	gcry_mpi_t key, peer;
-	gcry_error_t gcry;
-	guchar *buffer;
-	gsize n_buffer;
 	gboolean ret;
 
 	g_assert (self->pv->output);
 
-	if (!decode_output_mpi (self, "transport", "public", &peer))
+	if (!gkd_prompt_util_decode_mpi (self->pv->output, "transport", "public", &peer))
 		return FALSE;
 
 	ret = egg_dh_gen_key (peer, self->pv->secret, self->pv->prime, &key);
@@ -339,81 +273,19 @@ receive_transport_crypto (GkdPrompt *self)
 	if (!ret)
 		return FALSE;
 
-	/* Write the key out to raw data */
-	gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_buffer, peer);
-	g_return_val_if_fail (gcry == 0, FALSE);
-	buffer = egg_secure_alloc (n_buffer);
-	gcry = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, n_buffer, &n_buffer, peer);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	/* Allocate memory for hashed key */
 	egg_secure_free (self->pv->key);
-	g_assert (16 == gcry_md_get_algo_dlen (GCRY_MD_MD5));
-	self->pv->key = egg_secure_alloc (16);
-	self->pv->n_key = 16;
-
-	/* Use that as the input to derive a key for 128-bit AES */
-	gcry_md_hash_buffer (GCRY_MD_MD5, self->pv->key, buffer, n_buffer);
-
-	egg_secure_free (buffer);
-	return TRUE;
-}
-
-static gchar*
-decrypt_transport_crypto (GkdPrompt *self, guchar *data, gsize n_data,
-                          guchar *iv, gsize n_iv)
-{
-	gcry_cipher_hd_t cih;
-	gcry_error_t gcry;
-	gchar *result;
-	gsize pos;
-
-	g_assert (self->pv->key);
-	g_assert (self->pv->n_key == 16);
-
-	if (n_iv != 16) {
-		g_warning ("prompt response has iv of wrong length");
-		return NULL;
-	}
-
-	if (n_data % 16 != 0) {
-		g_warning ("prompt response encrypted password of wrong length");
-		return NULL;
-	}
-
-	gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
-	if (gcry) {
-		g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
-		return NULL;
-	}
-
-	/* 16 = 128 bits */
-	gcry = gcry_cipher_setkey (cih, self->pv->key, 16);
-	g_return_val_if_fail (gcry == 0, NULL);
-
-	/* 16 = 128 bits */
-	gcry = gcry_cipher_setiv (cih, iv, 16);
-	g_return_val_if_fail (gcry == 0, NULL);
-
-	/* Allocate memory for the result */
-	result = egg_secure_alloc (n_data);
-
-	for (pos = 0; pos < n_data; pos += 16) {
-		gcry = gcry_cipher_decrypt (cih, result + pos, 16, data + pos, 16);
-		g_return_val_if_fail (gcry == 0, NULL);
-	}
+	ret = gkd_prompt_util_mpi_to_key (key, &self->pv->key, &self->pv->n_key);
+	gcry_mpi_release (key);
 
-	gcry_cipher_close (cih);
-
-	if (!g_utf8_validate (result, n_data, NULL)) {
-		egg_secure_free (result);
-		return NULL;
+	if (!ret) {
+		self->pv->key = NULL;
+		self->pv->n_key = 0;
+		return FALSE;
 	}
 
-	return result;
+	return TRUE;
 }
 
-
 static gboolean
 prepare_input_data (GkdPrompt *self)
 {
@@ -739,21 +611,21 @@ gkd_prompt_get_password (GkdPrompt *self, const gchar *password_type)
 		g_return_val_if_reached (NULL);
 
 	/* Parse out an IV */
-	iv = decode_output_hex (self, password_type, "iv", &n_iv);
+	iv = gkd_prompt_util_decode_hex (self->pv->input, password_type, "iv", &n_iv);
 	if (iv == NULL) {
 		g_warning ("prompt response has encrypted password, but no iv set");
 		return NULL;
 	}
 
 	/* Parse out the password */
-	data = decode_output_hex (self, password_type, "value", &n_data);
+	data = gkd_prompt_util_decode_hex (self->pv->input, password_type, "value", &n_data);
 	if (data == NULL) {
 		g_warning ("prompt response missing encrypted password value");
 		g_free (iv);
 		return NULL;
 	}
 
-	result = decrypt_transport_crypto (self, data, n_data, iv, n_iv);
+	result = gkd_prompt_util_decrypt_text (self->pv->key, self->pv->n_key, iv, n_iv, data, n_data);
 	g_free (data);
 	g_free (iv);
 
diff --git a/daemon/prompt/tests/Makefile.am b/daemon/prompt/tests/Makefile.am
new file mode 100644
index 0000000..2717a64
--- /dev/null
+++ b/daemon/prompt/tests/Makefile.am
@@ -0,0 +1,14 @@
+# Test files should be listed in order they need to run
+UNIT_AUTO = \
+	unit-test-util.c
+
+UNIT_PROMPT =
+
+UNIT_LIBS =  \
+	$(top_builddir)/daemon/prompt/libgkd-prompt.la \
+	$(top_builddir)/egg/libegg-prompt.la
+
+EXTRA_DIST = \
+	test-data
+
+include $(top_srcdir)/tests/gtest.make
diff --git a/daemon/prompt/test/test-data/prompt-empty b/daemon/prompt/tests/test-data/prompt-empty
similarity index 100%
rename from daemon/prompt/test/test-data/prompt-empty
rename to daemon/prompt/tests/test-data/prompt-empty
diff --git a/daemon/prompt/test/test-data/prompt-full b/daemon/prompt/tests/test-data/prompt-full
similarity index 100%
rename from daemon/prompt/test/test-data/prompt-full
rename to daemon/prompt/tests/test-data/prompt-full
diff --git a/daemon/prompt/test/test-data/prompt-test b/daemon/prompt/tests/test-data/prompt-test
similarity index 100%
rename from daemon/prompt/test/test-data/prompt-test
rename to daemon/prompt/tests/test-data/prompt-test
diff --git a/daemon/prompt/tests/unit-test-util.c b/daemon/prompt/tests/unit-test-util.c
new file mode 100644
index 0000000..d3c2bde
--- /dev/null
+++ b/daemon/prompt/tests/unit-test-util.c
@@ -0,0 +1,146 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-util.c: Test gkd-prompt-util.c
+
+   Copyright (C) 2009 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 "run-auto-test.h"
+
+#include "gkd-prompt-util.h"
+
+#include <egg/egg-dh.h>
+#include <egg/egg-libgcrypt.h>
+#include <egg/egg-secure-memory.h>
+
+#include <gcrypt.h>
+
+static GKeyFile *key_file = NULL;
+
+DEFINE_SETUP(prompt_util)
+{
+	egg_libgcrypt_initialize ();
+	key_file = g_key_file_new ();
+}
+
+DEFINE_TEARDOWN(prompt_util)
+{
+	g_key_file_free (key_file);
+	key_file = NULL;
+}
+
+DEFINE_TEST(encode_decode_mpi)
+{
+	gcry_mpi_t mpi, check;
+
+	mpi = gcry_mpi_new (512);
+	gcry_mpi_randomize (mpi, 512, GCRY_WEAK_RANDOM);
+
+	gkd_prompt_util_encode_mpi (key_file, "section", "field", mpi);
+	if (!gkd_prompt_util_decode_mpi (key_file, "section", "field", &check))
+		g_assert_not_reached ();
+
+	g_assert (gcry_mpi_cmp (mpi, check) == 0);
+	gcry_mpi_release (mpi);
+	gcry_mpi_release (check);
+}
+
+DEFINE_TEST(decode_nonexistant_mpi)
+{
+	gcry_mpi_t mpi;
+
+	if (gkd_prompt_util_decode_mpi (key_file, "nonexist", "nope", &mpi))
+		g_assert_not_reached ();
+}
+
+DEFINE_TEST(encode_decode_hex)
+{
+	gchar buffer[32];
+	gpointer check;
+	gsize n_check;
+
+	gcry_create_nonce (buffer, 32);
+	gkd_prompt_util_encode_hex (key_file, "section", "field", buffer, 32);
+	check = gkd_prompt_util_decode_hex (key_file, "section", "field", &n_check);
+	g_assert (check);
+	g_assert (n_check == 32);
+	g_assert (memcmp (buffer, check, 32) == 0);
+
+	g_free (check);
+}
+
+DEFINE_TEST(decode_nonexistant_hex)
+{
+	gsize n_data;
+
+	if (gkd_prompt_util_decode_hex (key_file, "nonexist", "nope", &n_data))
+		g_assert_not_reached ();
+}
+
+static void
+do_encrypt_decrypt_text (const gchar *text)
+{
+	gcry_mpi_t mpi;
+	gpointer key, enc;
+	gsize n_key, n_enc;
+	guchar iv[16];
+	gchar *check;
+
+	g_test_message ("prompt encrypt/decrypt text: %s", text);
+
+	/* Test making a key */
+	mpi = gcry_mpi_new (512);
+	gcry_mpi_randomize (mpi, 512, GCRY_WEAK_RANDOM);
+
+	if (!gkd_prompt_util_mpi_to_key (mpi, &key, &n_key))
+		g_assert_not_reached ();
+
+	gcry_create_nonce (iv, 16);
+	enc = gkd_prompt_util_encrypt_text (key, n_key, iv, 16, text, &n_enc);
+	egg_secure_clear (key, n_key);
+	egg_secure_free (key);
+
+	g_assert (enc);
+	/* Always greater due to null term */
+	g_assert (n_enc > strlen (text));
+	g_assert (n_enc % 16 == 0);
+
+	if (!gkd_prompt_util_mpi_to_key (mpi, &key, &n_key))
+		g_assert_not_reached ();
+
+	check = gkd_prompt_util_decrypt_text (key, n_key, iv, 16, enc, n_enc);
+	egg_secure_clear (key, n_key);
+	egg_secure_free (key);
+	g_free (enc);
+
+	g_assert (check);
+	g_assert (strlen (check) < n_enc);
+	g_assert_cmpstr (check, ==, text);
+
+	gcry_mpi_release (mpi);
+}
+
+DEFINE_TEST(encrypt_decrypt_text)
+{
+	do_encrypt_decrypt_text ("");
+	do_encrypt_decrypt_text ("blah");
+	do_encrypt_decrypt_text ("0123456789ABCDEF");
+	do_encrypt_decrypt_text ("0123456789ABCDE");
+	do_encrypt_decrypt_text ("0123456789ABCDEF 12345");
+}
diff --git a/egg/Makefile.am b/egg/Makefile.am
index 584df42..9fe621c 100644
--- a/egg/Makefile.am
+++ b/egg/Makefile.am
@@ -5,6 +5,7 @@ noinst_LTLIBRARIES = \
 	libegg-creds.la \
 	libegg-dbus.la \
 	libegg-secure.la \
+	libegg-prompt.la \
 	libegg-secure-entry.la
 
 BUILT_SOURCES = \
@@ -67,7 +68,7 @@ libegg_creds_la_SOURCES = \
 	egg-unix-credentials.c egg-unix-credentials.h	
 
 libegg_dbus_la_SOURCES = \
-	egg-dbus.c egg-dbus.h 
+	egg-dbus.c egg-dbus.h
 
 libegg_dbus_la_CFLAGS = \
 	$(DBUS_CFLAGS) \
@@ -76,7 +77,22 @@ libegg_dbus_la_CFLAGS = \
 libegg_dbus_la_LIBADD = \
 	$(DBUS_LIBS) \
 	$(GLIB_LIBS)
-	
+
+libegg_prompt_la_SOURCES = \
+	egg-dh.c egg-dh.h \
+	egg-hex.c egg-hex.h \
+	egg-libgcrypt.c egg-libgcrypt.h \
+	egg-secure-memory.c egg-secure-memory.h
+
+libegg_prompt_la_CFLAGS = \
+	-DEGG_DH_NO_ASN1=1 \
+	$(LIBGCRYPT_CFLAGS) \
+	$(GLIB_CFLAGS)
+
+libegg_prompt_la_LIBS = \
+	$(LIBGCRYPT_LIBS) \
+	$(GLIB_LIBS)
+
 # -------------------------------------------------------------------
 
 if WITH_TESTS
diff --git a/egg/egg-dh.c b/egg/egg-dh.c
index ba917b4..ccb2243 100644
--- a/egg/egg-dh.c
+++ b/egg/egg-dh.c
@@ -107,6 +107,8 @@ typedef struct _Parameters {
 	gcry_mpi_t g;
 } Parameters;
 
+#ifndef EGG_DH_NO_ASN1
+
 static gboolean
 parse_der_pkcs3 (const guchar *data, gsize n_data, Parameters *params)
 {
@@ -165,3 +167,5 @@ egg_dh_parse_pkcs3 (const guchar *data, gsize n_data, gcry_mpi_t *p, gcry_mpi_t
 	*g = params.g;
 	return TRUE;
 }
+
+#endif /* EGG_DH_NO_ASN1 */
diff --git a/egg/egg-dh.h b/egg/egg-dh.h
index fd09bbc..eb7959d 100644
--- a/egg/egg-dh.h
+++ b/egg/egg-dh.h
@@ -32,6 +32,8 @@ gboolean   egg_dh_gen_secret       (gcry_mpi_t p, gcry_mpi_t g, gcry_mpi_t *X, g
 
 gboolean   egg_dh_gen_key          (gcry_mpi_t Y, gcry_mpi_t x, gcry_mpi_t p, gcry_mpi_t *k);
 
+#ifndef EGG_DH_NO_ASN1
 gboolean   egg_dh_parse_pkcs3      (const guchar *data, gsize n_data, gcry_mpi_t *p, gcry_mpi_t *g);
+#endif
 
 #endif /* EGG_DH_H_ */



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