[gnome-keyring] gcr: Implement GcrSecretExchange
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring] gcr: Implement GcrSecretExchange
- Date: Sat, 1 Oct 2011 11:19:47 +0000 (UTC)
commit 47b881feec299d56ada232c803d30f564063d792
Author: Stef Walter <stefw collabora co uk>
Date: Wed Aug 10 10:58:37 2011 +0200
gcr: Implement GcrSecretExchange
* Class which allows exchanging secrets over an unsecure or logged
channel.
* Does *NOT* protect against active attacks like MITM.
https://bugzilla.gnome.org/show_bug.cgi?id=656955
gcr/Makefile.am | 2 +
gcr/gcr-secret-exchange.c | 523 +++++++++++++++++++++++++++++++++++++++++++++
gcr/gcr-secret-exchange.h | 78 +++++++
3 files changed, 603 insertions(+), 0 deletions(-)
---
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 6101f16..c29fd71 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -25,6 +25,7 @@ HEADER_BASE_FILES = \
gcr-library.h \
gcr-parser.h \
gcr-pkcs11-certificate.h \
+ gcr-secret-exchange.h \
gcr-simple-certificate.h \
gcr-trust.h \
gcr-types.h \
@@ -116,6 +117,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \
gcr-pkcs11-importer.c gcr-pkcs11-importer.h \
gcr-record.c gcr-record.h \
+ gcr-secret-exchange.c gcr-secret-exchange.h \
gcr-simple-certificate.c gcr-simple-certificate.h \
gcr-simple-collection.c gcr-simple-collection.h \
gcr-single-collection.c gcr-single-collection.h \
diff --git a/gcr/gcr-secret-exchange.c b/gcr/gcr-secret-exchange.c
new file mode 100644
index 0000000..768083c
--- /dev/null
+++ b/gcr/gcr-secret-exchange.c
@@ -0,0 +1,523 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 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.
+ */
+
+#include "config.h"
+
+#include "gcr-secret-exchange.h"
+
+#include "egg/egg-dh.h"
+#include "egg/egg-hkdf.h"
+#include "egg/egg-libgcrypt.h"
+#include "egg/egg-padding.h"
+#include "egg/egg-secure-memory.h"
+
+#include <string.h>
+#include <gcrypt.h>
+
+/*
+ * This is the only set we support so far. It includes:
+ * - DH with the 1536 ike modp group for key exchange
+ * - HKDF SHA256 for hashing of the key to appropriate size
+ * - AES 128 CBC for encryption
+ * - PKCS#7 style padding
+ */
+
+#define EXCHANGE_VERSION "secret-exchange-1"
+
+#define EXCHANGE_1_IKE_NAME "ietf-ike-grp-modp-1536"
+#define EXCHANGE_1_KEY_LENGTH 16
+#define EXCHANGE_1_IV_LENGTH 16
+#define EXCHANGE_1_HASH_ALGO "sha256"
+#define EXCHANGE_1_CIPHER_ALGO GCRY_CIPHER_AES128
+#define EXCHANGE_1_CIPHER_MODE GCRY_CIPHER_MODE_CBC
+
+struct _GcrSecretExchangePrivate {
+ gcry_mpi_t prime;
+ gcry_mpi_t base;
+ gcry_mpi_t priv;
+ guchar *secret;
+ gsize n_secret;
+};
+
+G_DEFINE_TYPE (GcrSecretExchange, gcr_secret_exchange, G_TYPE_OBJECT);
+
+static void
+key_file_set_base64 (GKeyFile *key_file, const gchar *section,
+ const gchar *field, gconstpointer data, gsize n_data)
+{
+ gchar *value;
+
+ value = g_base64_encode (data, n_data);
+ g_key_file_set_value (key_file, section, field, value);
+ g_free (value);
+}
+
+static gpointer
+key_file_get_base64 (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 = g_base64_decode (data, n_result);
+ g_free (data);
+ return result;
+}
+
+static void
+key_file_set_mpi (GKeyFile *key_file, const gchar *section,
+ const gchar *field, gcry_mpi_t mpi)
+{
+ gcry_error_t gcry;
+ guchar *data;
+ gsize n_data;
+
+ /* Get the size */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_data, mpi);
+ g_return_if_fail (gcry == 0);
+
+ data = g_malloc0 (n_data);
+
+ /* Write into buffer */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, n_data, &n_data, mpi);
+ g_return_if_fail (gcry == 0);
+
+ key_file_set_base64 (key_file, section, field, data, n_data);
+ g_free (data);
+}
+
+static gcry_mpi_t
+key_file_get_mpi (GKeyFile *key_file, const gchar *section,
+ const gchar *field)
+{
+ gcry_mpi_t mpi;
+ gcry_error_t gcry;
+ gpointer data;
+ gsize n_data;
+
+ g_return_val_if_fail (key_file, FALSE);
+ g_return_val_if_fail (section, FALSE);
+ g_return_val_if_fail (field, FALSE);
+
+ data = key_file_get_base64 (key_file, section, field, &n_data);
+ if (data == NULL)
+ return FALSE;
+
+ gcry = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, data, n_data, NULL);
+ g_free (data);
+
+ return (gcry == 0) ? mpi : NULL;
+}
+
+/* ----------------------------------------------------------------------------
+ * REQUESTER SIDE
+ */
+
+static void
+gcr_secret_exchange_init (GcrSecretExchange *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SECRET_EXCHANGE,
+ GcrSecretExchangePrivate);
+
+ if (!egg_dh_default_params (EXCHANGE_1_IKE_NAME, &self->pv->prime, &self->pv->base))
+ g_return_if_reached ();
+}
+
+static void
+clear_secret_exchange (GcrSecretExchange *self)
+{
+ if (self->pv->priv) {
+ gcry_mpi_release (self->pv->priv);
+ self->pv->priv = NULL;
+ }
+ egg_secure_free (self->pv->secret);
+ self->pv->secret = NULL;
+ self->pv->n_secret = 0;
+}
+
+static void
+gcr_secret_exchange_finalize (GObject *obj)
+{
+ GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
+
+ clear_secret_exchange (self);
+ gcry_mpi_release (self->pv->priv);
+
+ G_OBJECT_CLASS (gcr_secret_exchange_parent_class)->finalize (obj);
+}
+
+static void
+gcr_secret_exchange_class_init (GcrSecretExchangeClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = gcr_secret_exchange_finalize;
+ g_type_class_add_private (gobject_class, sizeof (GcrSecretExchangePrivate));
+
+ egg_libgcrypt_initialize ();
+}
+
+GcrSecretExchange *
+gcr_secret_exchange_new (void)
+{
+ return g_object_new (GCR_TYPE_SECRET_EXCHANGE, NULL);
+}
+
+gchar *
+gcr_secret_exchange_request (GcrSecretExchange *self)
+{
+ GKeyFile *output;
+ gcry_mpi_t pub;
+ gchar *result;
+
+ g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+
+ clear_secret_exchange (self);
+
+ output = g_key_file_new ();
+
+ if (!egg_dh_gen_pair (self->pv->prime, self->pv->base, 0, &pub, &self->pv->priv))
+ g_return_val_if_reached (NULL);
+
+ key_file_set_mpi (output, EXCHANGE_VERSION, "public", pub);
+ gcry_mpi_release (pub);
+
+ result = g_key_file_to_data (output, NULL, NULL);
+ g_return_val_if_fail (result != NULL, NULL);
+
+ g_key_file_free (output);
+
+ return result;
+}
+
+static gpointer
+calculate_receive_key (GKeyFile *input, gcry_mpi_t prime, gcry_mpi_t priv)
+{
+ gcry_mpi_t peer;
+ gpointer ikm;
+ gsize n_ikm;
+ gpointer key;
+
+ peer = key_file_get_mpi (input, EXCHANGE_VERSION, "public");
+ if (peer == NULL) {
+ g_message ("secret-exchange: invalid or missing 'public' argument");
+ return NULL;
+ }
+
+ /* Build up a key we can use */
+ ikm = egg_dh_gen_secret (peer, priv, prime, &n_ikm);
+ g_return_val_if_fail (ikm != NULL, NULL);
+
+ key = egg_secure_alloc (EXCHANGE_1_KEY_LENGTH);
+ if (!egg_hkdf_perform (EXCHANGE_1_HASH_ALGO, ikm, n_ikm, NULL, 0,
+ NULL, 0, key, EXCHANGE_1_KEY_LENGTH))
+ g_return_val_if_reached (NULL);
+
+ egg_secure_free (ikm);
+ gcry_mpi_release (peer);
+
+ return key;
+}
+
+static gpointer
+perform_aes_decrypt (GKeyFile *input,
+ gcry_mpi_t prime,
+ gcry_mpi_t priv,
+ gsize *n_secret)
+{
+ gcry_cipher_hd_t cih;
+ gcry_error_t gcry;
+ guchar* padded;
+ guchar* result;
+ gpointer key;
+ gpointer iv;
+ gpointer value;
+ gsize n_result;
+ gsize n_iv;
+ gsize n_value;
+ gsize pos;
+
+ iv = key_file_get_base64 (input, EXCHANGE_VERSION, "iv", &n_iv);
+ if (iv == NULL || n_iv != EXCHANGE_1_IV_LENGTH) {
+ g_message ("secret-exchange: invalid or missing iv");
+ return NULL;
+ }
+
+ value = key_file_get_base64 (input, EXCHANGE_VERSION, "value", &n_value);
+ if (value == NULL) {
+ g_message ("secret-exchange: invalid or missing value");
+ g_free (iv);
+ return NULL;
+ }
+
+ key = calculate_receive_key (input, prime, priv);
+ if (key == NULL) {
+ g_free (iv);
+ g_free (value);
+ return NULL;
+ }
+
+ gcry = gcry_cipher_open (&cih, EXCHANGE_1_CIPHER_ALGO, EXCHANGE_1_CIPHER_MODE, 0);
+ if (gcry != 0) {
+ g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
+ egg_secure_free (key);
+ g_free (iv);
+ return FALSE;
+ }
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setkey (cih, key, EXCHANGE_1_KEY_LENGTH);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setiv (cih, iv, EXCHANGE_1_IV_LENGTH);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ egg_secure_free (key);
+ g_free (iv);
+
+ /* Allocate memory for the result */
+ padded = egg_secure_alloc (n_value);
+
+ for (pos = 0; pos < n_value; pos += 16) {
+ gcry = gcry_cipher_decrypt (cih, padded + pos, 16, (guchar*)value + pos, 16);
+ g_return_val_if_fail (gcry == 0, NULL);
+ }
+
+ gcry_cipher_close (cih);
+
+ /* This does an extra null-terminator of output */
+ if (!egg_padding_pkcs7_unpad (egg_secure_realloc, 16, padded, n_value,
+ (gpointer*)&result, &n_result))
+ result = NULL;
+
+ egg_secure_free (padded);
+
+ *n_secret = n_result;
+ return result;
+}
+
+gboolean
+gcr_secret_exchange_receive (GcrSecretExchange *self,
+ const gchar *response)
+{
+ GKeyFile *input;
+ guchar *secret;
+ gsize n_secret;
+
+ /* Parse the input */
+ input = g_key_file_new ();
+ if (!g_key_file_load_from_data (input, response, strlen (response),
+ G_KEY_FILE_NONE, NULL)) {
+ g_key_file_free (input);
+ g_message ("couldn't parse secret exchange request data");
+ return FALSE;
+ }
+
+ secret = perform_aes_decrypt (input, self->pv->prime, self->pv->priv, &n_secret);
+ g_key_file_free (input);
+
+ if (secret != NULL) {
+ egg_secure_free (self->pv->secret);
+ self->pv->secret = secret;
+ self->pv->n_secret = n_secret;
+ }
+
+ return (secret != NULL);
+}
+
+const guchar *
+gcr_secret_exchange_get_response (GcrSecretExchange *self,
+ gsize *secret_len)
+{
+ g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+
+ if (secret_len)
+ *secret_len = self->pv->n_secret;
+ return self->pv->secret;
+}
+
+/* ----------------------------------------------------------------------------
+ * RESPONDER SIDE
+ */
+
+static gpointer
+calculate_response_key (GKeyFile *input, GKeyFile *output)
+{
+ gcry_mpi_t prime;
+ gcry_mpi_t base;
+ gcry_mpi_t pub;
+ gcry_mpi_t priv;
+ gcry_mpi_t peer;
+ gpointer ikm;
+ gsize n_ikm;
+ gpointer key;
+
+ peer = key_file_get_mpi (input, EXCHANGE_VERSION, "public");
+ if (peer == NULL) {
+ g_message ("secret-exchange: invalid or missing 'public' argument");
+ return NULL;
+ }
+
+ if (!egg_dh_default_params (EXCHANGE_1_IKE_NAME, &prime, &base))
+ g_return_val_if_reached (NULL);
+
+ /* Generate our own public/priv, and then a key, send it back */
+ if (!egg_dh_gen_pair (prime, base, 0, &pub, &priv))
+ g_return_val_if_reached (NULL);
+
+ /* Build up a key we can use */
+ ikm = egg_dh_gen_secret (peer, priv, prime, &n_ikm);
+ g_return_val_if_fail (ikm != NULL, NULL);
+
+ key = egg_secure_alloc (EXCHANGE_1_KEY_LENGTH);
+ if (!egg_hkdf_perform (EXCHANGE_1_HASH_ALGO, ikm, n_ikm, NULL, 0,
+ NULL, 0, key, EXCHANGE_1_KEY_LENGTH))
+ g_return_val_if_reached (NULL);
+
+ key_file_set_mpi (output, EXCHANGE_VERSION, "public", pub);
+
+ egg_secure_free (ikm);
+ gcry_mpi_release (prime);
+ gcry_mpi_release (base);
+ gcry_mpi_release (peer);
+ gcry_mpi_release (pub);
+ gcry_mpi_release (priv);
+
+ return key;
+}
+
+static gpointer
+calculate_response_iv (GKeyFile *input, GKeyFile *output)
+{
+ gpointer iv;
+
+ iv = g_malloc0 (EXCHANGE_1_IV_LENGTH);
+ gcry_create_nonce (iv, EXCHANGE_1_IV_LENGTH);
+ key_file_set_base64 (output, EXCHANGE_VERSION, "iv", iv, EXCHANGE_1_IV_LENGTH);
+
+ return iv;
+}
+
+static gboolean
+perform_aes_encrypt (GKeyFile *input, GKeyFile *output,
+ gconstpointer secret, gsize n_secret)
+{
+ gcry_cipher_hd_t cih;
+ gcry_error_t gcry;
+ guchar* padded;
+ guchar* result;
+ gpointer key;
+ gpointer iv;
+ gsize n_result;
+ gsize pos;
+
+ key = calculate_response_key (input, output);
+ if (key == NULL)
+ return FALSE;
+
+ iv = calculate_response_iv (input, output);
+ g_return_val_if_fail (iv != NULL, FALSE);
+
+ gcry = gcry_cipher_open (&cih, EXCHANGE_1_CIPHER_ALGO, EXCHANGE_1_CIPHER_MODE, 0);
+ if (gcry != 0) {
+ g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
+ egg_secure_free (key);
+ g_free (iv);
+ return FALSE;
+ }
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setkey (cih, key, EXCHANGE_1_KEY_LENGTH);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setiv (cih, iv, EXCHANGE_1_IV_LENGTH);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ egg_secure_free (key);
+ g_free (iv);
+
+ /* Pad the text properly */
+ if (!egg_padding_pkcs7_pad (egg_secure_realloc, 16, secret, n_secret,
+ (gpointer*)&padded, &n_result))
+ g_return_val_if_reached (FALSE);
+ result = g_malloc0 (n_result);
+
+ 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, FALSE);
+ }
+
+ gcry_cipher_close (cih);
+
+ egg_secure_clear (padded, n_result);
+ egg_secure_free (padded);
+
+ key_file_set_base64 (output, EXCHANGE_VERSION, "value", result, n_result);
+ g_free (result);
+
+ return TRUE;
+}
+
+gchar *
+gcr_secret_exchange_respond (const gchar *request,
+ const guchar *secret,
+ gssize secret_len)
+{
+ GKeyFile *input;
+ GKeyFile *output;
+ gchar *result;
+
+ g_return_val_if_fail (request, NULL);
+ g_return_val_if_fail (secret, NULL);
+
+ if (secret_len < 0)
+ secret_len = strlen ((gchar *)secret);
+
+ /* Parse the input */
+ input = g_key_file_new ();
+ if (!g_key_file_load_from_data (input, request, strlen (request),
+ G_KEY_FILE_NONE, NULL)) {
+ g_key_file_free (input);
+ g_message ("couldn't parse secret exchange request data");
+ return NULL;
+ }
+
+ output = g_key_file_new ();
+
+ if (perform_aes_encrypt (input, output, secret, secret_len)) {
+ result = g_key_file_to_data (output, NULL, NULL);
+ g_return_val_if_fail (result != NULL, NULL);
+ }
+
+ g_key_file_free (input);
+ g_key_file_free (output);
+
+ return result;
+}
diff --git a/gcr/gcr-secret-exchange.h b/gcr/gcr-secret-exchange.h
new file mode 100644
index 0000000..8ec8264
--- /dev/null
+++ b/gcr/gcr-secret-exchange.h
@@ -0,0 +1,78 @@
+/*
+ * 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>
+ */
+
+#ifndef __GCR_SECRET_EXCHANGE_H__
+#define __GCR_SECRET_EXCHANGE_H__
+
+#include "gcr.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_SECRET_EXCHANGE (gcr_secret_exchange_get_type ())
+#define GCR_SECRET_EXCHANGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_COLLECTION, GcrSecretExchange))
+#define GCR_SECRET_EXCHANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_COLLECTION, GcrSecretExchangeClass))
+#define GCR_IS_SECRET_EXCHANGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_COLLECTION))
+#define GCR_IS_SECRET_EXCHANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_COLLECTION))
+#define GCR_SECRET_EXCHANGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_COLLECTION, GcrSecretExchangeClass))
+
+typedef struct _GcrSecretExchange GcrSecretExchange;
+typedef struct _GcrSecretExchangeClass GcrSecretExchangeClass;
+typedef struct _GcrSecretExchangePrivate GcrSecretExchangePrivate;
+
+struct _GcrSecretExchange {
+ GObject parent;
+
+ /*< private >*/
+ GcrSecretExchangePrivate *pv;
+};
+
+struct _GcrSecretExchangeClass {
+ GObjectClass parent_class;
+};
+
+/* Caller side functions */
+
+GType gcr_secret_exchange_get_type (void);
+
+GcrSecretExchange * gcr_secret_exchange_new (void);
+
+gchar * gcr_secret_exchange_request (GcrSecretExchange *self);
+
+gboolean gcr_secret_exchange_receive (GcrSecretExchange *self,
+ const gchar *response);
+
+const guchar * gcr_secret_exchange_get_response (GcrSecretExchange *self,
+ gsize *secret_len);
+
+/* Responder side functions */
+
+gchar * gcr_secret_exchange_respond (const gchar *request,
+ const guchar *secret,
+ gssize secret_len);
+
+
+G_END_DECLS
+
+#endif /* __GCR_SECRET_EXCHANGE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]