[gnome-keyring] gcr: More work on the GcrSecretExchange
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring] gcr: More work on the GcrSecretExchange
- Date: Sat, 1 Oct 2011 11:20:02 +0000 (UTC)
commit 03191799af4ee3bd7109151118e77593f6e6b445
Author: Stef Walter <stefw collabora co uk>
Date: Sat Oct 1 12:20:07 2011 +0200
gcr: More work on the GcrSecretExchange
* Expose the concept of which protocol is being used although
currently only one is supported.
* Add virtual method hooks so all crypto stuff can be swapped
out with other implementations.
* Build, documentation and test fixes
https://bugzilla.gnome.org/show_bug.cgi?id=656955
gcr/Makefile.am | 2 +-
gcr/gcr-base.symbols | 7 +
gcr/gcr-secret-exchange.c | 699 ++++++++++++++++++++++++++------------
gcr/gcr-secret-exchange.h | 38 ++-
gcr/tests/test-secret-exchange.c | 4 +-
5 files changed, 526 insertions(+), 224 deletions(-)
---
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index c29fd71..be21651 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -184,7 +184,6 @@ libgcr_ GCR_MAJOR@_la_LDFLAGS = \
libgcr_base_ GCR_MAJOR@_la_LIBADD = \
$(top_builddir)/egg/libegg.la \
- $(top_builddir)/egg/libegg-entry-buffer.la \
$(top_builddir)/gck/libgck- GCK_MAJOR@.la \
$(GOBJECT_LIBS) \
$(GLIB_LIBS) \
@@ -194,6 +193,7 @@ libgcr_base_ GCR_MAJOR@_la_LIBADD = \
libgcr_ GCR_MAJOR@_la_LIBADD = \
$(GTK_LIBS) \
$(libgcr_base_ GCR_MAJOR@_la_LIBADD) \
+ $(top_builddir)/egg/libegg-entry-buffer.la \
$(builddir)/libgcr-base-$(GCR_MAJOR).la
noinst_LTLIBRARIES = libgcr-testable.la
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index d1fca06..233ec15 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -111,6 +111,13 @@ gcr_pkcs11_initialize_finish
gcr_pkcs11_set_modules
gcr_pkcs11_set_trust_lookup_uris
gcr_pkcs11_set_trust_store_uri
+gcr_secret_exchange_begin
+gcr_secret_exchange_get_protocol
+gcr_secret_exchange_get_secret
+gcr_secret_exchange_get_type
+gcr_secret_exchange_new
+gcr_secret_exchange_receive
+gcr_secret_exchange_send
gcr_simple_certificate_get_type
gcr_simple_certificate_new
gcr_simple_certificate_new_static
diff --git a/gcr/gcr-secret-exchange.c b/gcr/gcr-secret-exchange.c
index 869b6de..6f6d5c6 100644
--- a/gcr/gcr-secret-exchange.c
+++ b/gcr/gcr-secret-exchange.c
@@ -32,6 +32,8 @@
#include <string.h>
#include <gcrypt.h>
+EGG_SECURE_DECLARE (secret_exchange);
+
/**
* SECTION:gcr-secret-exchange
* @title: GcrSecretExchange
@@ -72,29 +74,29 @@
* The class for #GcrSecretExchange
*/
-/*
- * 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
+/**
+ * GCR_SECRET_EXCHANGE_PROTOCOL_1:
+ *
+ * The current secret exchange protocol. Key agreement is done using DH with the
+ * 1536 bit IKE parameter group. Keys are derived using SHA256 with HKDF. The
+ * transport encryption is done with 128 bit AES.
*/
-#define EXCHANGE_VERSION "secret-exchange-1"
+enum {
+ PROP_0,
+ PROP_PROTOCOL
+};
-#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
+typedef struct _GcrSecretExchangeDefault GcrSecretExchangeDefault;
struct _GcrSecretExchangePrivate {
- gcry_mpi_t prime;
- gcry_mpi_t base;
- gcry_mpi_t pub;
- gcry_mpi_t priv;
- gpointer key;
+ GcrSecretExchangeDefault *default_exchange;
+ GDestroyNotify destroy_exchange;
+ gboolean explicit_protocol;
+ gboolean generated;
+ guchar *publi;
+ gsize n_publi;
+ gboolean derived;
gchar *secret;
gsize n_secret;
};
@@ -132,69 +134,65 @@ key_file_get_base64 (GKeyFile *key_file, const gchar *section,
}
static void
-key_file_set_mpi (GKeyFile *key_file, const gchar *section,
- const gchar *field, gcry_mpi_t mpi)
+gcr_secret_exchange_init (GcrSecretExchange *self)
{
- 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);
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SECRET_EXCHANGE,
+ GcrSecretExchangePrivate);
}
-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;
+static void
+gcr_secret_exchange_set_property (GObject *obj,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
+ const gchar *protocol;
+
+ switch (prop_id) {
+ case PROP_PROTOCOL:
+ protocol = g_value_get_string (value);
+ if (protocol != NULL) {
+ if (g_str_equal (protocol, GCR_SECRET_EXCHANGE_PROTOCOL_1))
+ self->pv->explicit_protocol = TRUE;
+ else
+ g_warning ("the GcrSecretExchange protocol %s is unsupported defaulting to %s",
+ protocol, GCR_SECRET_EXCHANGE_PROTOCOL_1);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
}
static void
-gcr_secret_exchange_init (GcrSecretExchange *self)
+gcr_secret_exchange_get_property (GObject *obj,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SECRET_EXCHANGE,
- GcrSecretExchangePrivate);
+ GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
- if (!egg_dh_default_params (EXCHANGE_1_IKE_NAME, &self->pv->prime, &self->pv->base))
- g_return_if_reached ();
+ switch (prop_id) {
+ case PROP_PROTOCOL:
+ g_value_set_string (value, gcr_secret_exchange_get_protocol (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
}
static void
clear_secret_exchange (GcrSecretExchange *self)
{
- gcry_mpi_release (self->pv->priv);
- self->pv->priv = NULL;
- gcry_mpi_release (self->pv->pub);
- self->pv->pub = NULL;
- egg_secure_free (self->pv->key);
- self->pv->key = NULL;
+ g_free (self->pv->publi);
+ self->pv->publi = NULL;
+ self->pv->n_publi = 0;
+ self->pv->derived = FALSE;
+ self->pv->generated = TRUE;
egg_secure_free (self->pv->secret);
self->pv->secret = NULL;
self->pv->n_secret = 0;
@@ -205,35 +203,53 @@ gcr_secret_exchange_finalize (GObject *obj)
{
GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
+ if (self->pv->destroy_exchange)
+ (self->pv->destroy_exchange) (self->pv->default_exchange);
+
clear_secret_exchange (self);
- gcry_mpi_release (self->pv->prime);
- gcry_mpi_release (self->pv->base);
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 ();
-}
-
/**
* gcr_secret_exchange_new:
+ * @protocol: (allow-none): the exchange protocol to use
*
* Create a new secret exchange object.
*
+ * Specify a protocol of %NULL to allow any protocol. This is especially
+ * relevant on the side of the exchange that does not call
+ * gcr_secret_exchange_begin(), that is the originator. Currently the only
+ * protocol supported is %GCR_SECRET_EXCHANGE_PROTOCOL_1.
+ *
* Returns: (transfer full): A new #GcrSecretExchange object
*/
GcrSecretExchange *
-gcr_secret_exchange_new (void)
+gcr_secret_exchange_new (const gchar *protocol)
{
- return g_object_new (GCR_TYPE_SECRET_EXCHANGE, NULL);
+ return g_object_new (GCR_TYPE_SECRET_EXCHANGE,
+ "protocol", protocol,
+ NULL);
+}
+
+/**
+ * gcr_secret_exchange_get_protocol:
+ *
+ * Get the secret exchange protocol.
+ *
+ * Will return %NULL if no protocol was specified, and either
+ * gcr_secret_exchange_begin() or gcr_secret_exchange_receive() have not been
+ * called successfully.
+ *
+ * Returns: the protocol or %NULL
+ */
+const gchar *
+gcr_secret_exchange_get_protocol (GcrSecretExchange *self)
+{
+ g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+ if (self->pv->explicit_protocol || self->pv->generated)
+ return GCR_SECRET_EXCHANGE_PROTOCOL_1;
+ return NULL;
}
/**
@@ -250,21 +266,26 @@ gcr_secret_exchange_new (void)
gchar *
gcr_secret_exchange_begin (GcrSecretExchange *self)
{
+ GcrSecretExchangeClass *klass;
GKeyFile *output;
gchar *result;
g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+ klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
+ g_return_val_if_fail (klass->generate_exchange_key, NULL);
+
clear_secret_exchange (self);
- g_assert (self->pv->priv == NULL);
output = g_key_file_new ();
- if (!egg_dh_gen_pair (self->pv->prime, self->pv->base, 0,
- &self->pv->pub, &self->pv->priv))
+ if (!(klass->generate_exchange_key) (self, GCR_SECRET_EXCHANGE_PROTOCOL_1,
+ &self->pv->publi, &self->pv->n_publi))
g_return_val_if_reached (NULL);
+ self->pv->generated = TRUE;
- key_file_set_mpi (output, EXCHANGE_VERSION, "public", self->pv->pub);
+ key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public",
+ self->pv->publi, self->pv->n_publi);
result = g_key_file_to_data (output, NULL, NULL);
g_return_val_if_fail (result != NULL, NULL);
@@ -278,98 +299,70 @@ static gboolean
calculate_key (GcrSecretExchange *self,
GKeyFile *input)
{
- gcry_mpi_t peer;
- gpointer ikm;
- gsize n_ikm;
+ GcrSecretExchangeClass *klass;
+ gboolean ret;
+ guchar *peer;
+ gsize n_peer;
+
+ klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
+ g_return_val_if_fail (klass->derive_transport_key, FALSE);
- peer = key_file_get_mpi (input, EXCHANGE_VERSION, "public");
+ peer = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public", &n_peer);
if (peer == NULL) {
g_message ("secret-exchange: invalid or missing 'public' argument");
return FALSE;
}
- /* Build up a key we can use */
- ikm = egg_dh_gen_secret (peer, self->pv->priv, self->pv->prime, &n_ikm);
- g_return_val_if_fail (ikm != NULL, FALSE);
-
- if (self->pv->key == NULL)
- self->pv->key = egg_secure_alloc (EXCHANGE_1_KEY_LENGTH);
-
- if (!egg_hkdf_perform (EXCHANGE_1_HASH_ALGO, ikm, n_ikm, NULL, 0,
- NULL, 0, self->pv->key, EXCHANGE_1_KEY_LENGTH))
- g_return_val_if_reached (FALSE);
-
- egg_secure_free (ikm);
- gcry_mpi_release (peer);
+ ret = (klass->derive_transport_key) (self, peer, n_peer);
+ self->pv->derived = ret;
- return TRUE;
+ g_free (peer);
+ return ret;
}
-static gpointer
-perform_aes_decrypt (GcrSecretExchange *self,
- GKeyFile *input,
- gsize *n_secret)
+static gboolean
+perform_decrypt (GcrSecretExchange *self,
+ GKeyFile *input,
+ guchar **secret,
+ gsize *n_secret)
{
- gcry_cipher_hd_t cih;
- gcry_error_t gcry;
- guchar* padded;
- guchar* result;
- gpointer iv;
- gpointer value;
- gsize n_result;
- gsize n_iv;
- gsize n_value;
- gsize pos;
+ GcrSecretExchangeClass *klass;
+ gpointer iv, value;
+ guchar *result;
+ gsize n_result, n_iv, n_value;
+ gboolean ret;
- 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;
- }
+ klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
+ g_return_val_if_fail (klass->decrypt_transport_data, FALSE);
- value = key_file_get_base64 (input, EXCHANGE_VERSION, "secret", &n_value);
+ iv = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "iv", &n_iv);
+
+ value = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", &n_value);
if (value == NULL) {
g_message ("secret-exchange: invalid or missing value");
g_free (iv);
- 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));
- g_free (iv);
return FALSE;
}
- /* 16 = 128 bits */
- gcry = gcry_cipher_setkey (cih, self->pv->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);
+ ret = (klass->decrypt_transport_data) (self, egg_secure_realloc, value, n_value,
+ iv, n_iv, &result, &n_result);
+ g_free (value);
g_free (iv);
- /* Allocate memory for the result */
- padded = egg_secure_alloc (n_value);
+ if (!ret)
+ return FALSE;
- 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);
+ /* Reallocate a null terminator */
+ if (result) {
+ result = egg_secure_realloc (result, n_result + 1);
+ result[n_result] = 0;
}
- 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);
-
+ *secret = result;
*n_secret = n_result;
- return result;
+
+ return TRUE;
}
/**
@@ -386,11 +379,19 @@ gboolean
gcr_secret_exchange_receive (GcrSecretExchange *self,
const gchar *exchange)
{
+ GcrSecretExchangeClass *klass;
GKeyFile *input;
gchar *secret;
gsize n_secret;
gboolean ret;
+ g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), FALSE);
+ g_return_val_if_fail (exchange != NULL, FALSE);
+
+ klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
+ g_return_val_if_fail (klass->generate_exchange_key, FALSE);
+ g_return_val_if_fail (klass->derive_transport_key, FALSE);
+
/* Parse the input */
input = g_key_file_new ();
if (!g_key_file_load_from_data (input, exchange, strlen (exchange),
@@ -400,10 +401,11 @@ gcr_secret_exchange_receive (GcrSecretExchange *self,
return FALSE;
}
- if (self->pv->priv == NULL) {
- if (!egg_dh_gen_pair (self->pv->prime, self->pv->base, 0,
- &self->pv->pub, &self->pv->priv))
+ if (!self->pv->generated) {
+ if (!(klass->generate_exchange_key) (self, GCR_SECRET_EXCHANGE_PROTOCOL_1,
+ &self->pv->publi, &self->pv->n_publi))
g_return_val_if_reached (FALSE);
+ self->pv->generated = TRUE;
}
if (!calculate_key (self, input))
@@ -411,9 +413,10 @@ gcr_secret_exchange_receive (GcrSecretExchange *self,
ret = TRUE;
- if (g_key_file_has_key (input, EXCHANGE_VERSION, "secret", NULL)) {
- secret = perform_aes_decrypt (self, input, &n_secret);
- if (secret == NULL) {
+ if (g_key_file_has_key (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", NULL)) {
+
+ /* Remember that this can return a NULL secret */
+ if (!perform_decrypt (self, input, (guchar **)&secret, &n_secret)) {
ret = FALSE;
} else {
egg_secure_free (self->pv->secret);
@@ -440,7 +443,7 @@ gcr_secret_exchange_receive (GcrSecretExchange *self,
* so if you're certain that it is does not contain arbitrary binary data,
* it can be used as a string.
*
- * Returns: (transfer none): The last secret received.
+ * Returns: (transfer none) (array length=secret_len): the last secret received
*/
const gchar *
gcr_secret_exchange_get_secret (GcrSecretExchange *self,
@@ -453,70 +456,28 @@ gcr_secret_exchange_get_secret (GcrSecretExchange *self,
return self->pv->secret;
}
-static gpointer
-calculate_iv (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 *output,
- gconstpointer key,
- const gchar *secret,
- gsize n_secret)
+perform_encrypt (GcrSecretExchange *self,
+ GKeyFile *output,
+ const gchar *secret,
+ gsize n_secret)
{
- gcry_cipher_hd_t cih;
- gcry_error_t gcry;
- guchar* padded;
- guchar* result;
- gpointer iv;
- gsize n_result;
- gsize pos;
+ GcrSecretExchangeClass *klass;
+ guchar *result, *iv;
+ gsize n_result, n_iv;
- iv = calculate_iv (output);
- g_return_val_if_fail (iv != NULL, FALSE);
+ klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
+ g_return_val_if_fail (klass->encrypt_transport_data, 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));
- g_free (iv);
+ if (!(klass->encrypt_transport_data) (self, g_realloc, (const guchar *)secret,
+ n_secret, &iv, &n_iv, &result, &n_result))
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);
-
- 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);
+ key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", result, n_result);
+ key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "iv", iv, n_iv);
- egg_secure_clear (padded, n_result);
- egg_secure_free (padded);
-
- key_file_set_base64 (output, EXCHANGE_VERSION, "secret", result, n_result);
g_free (result);
+ g_free (iv);
return TRUE;
}
@@ -547,19 +508,20 @@ gcr_secret_exchange_send (GcrSecretExchange *self,
g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
- if (self->pv->key == NULL) {
+ if (!self->pv->derived) {
g_warning ("gcr_secret_exchange_receive() must be called "
"before calling this function");
return NULL;
}
output = g_key_file_new ();
- key_file_set_mpi (output, EXCHANGE_VERSION, "public", self->pv->pub);
+ key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public", self->pv->publi,
+ self->pv->n_publi);
if (secret != NULL) {
if (secret_len < 0)
secret_len = strlen (secret);
- if (!perform_aes_encrypt (output, self->pv->key, secret, secret_len)) {
+ if (!perform_encrypt (self, output, secret, secret_len)) {
g_key_file_free (output);
return NULL;
}
@@ -570,3 +532,302 @@ gcr_secret_exchange_send (GcrSecretExchange *self,
g_key_file_free (output);
return result;
}
+
+/*
+ * 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_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 _GcrSecretExchangeDefault {
+ gcry_mpi_t prime;
+ gcry_mpi_t base;
+ gcry_mpi_t pub;
+ gcry_mpi_t priv;
+ gpointer key;
+};
+
+static guchar *
+mpi_to_data (gcry_mpi_t mpi,
+ gsize *n_data)
+{
+ gcry_error_t gcry;
+ guchar *data;
+
+ /* Get the size */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, n_data, mpi);
+ g_return_val_if_fail (gcry == 0, NULL);
+
+ data = g_malloc0 (*n_data);
+
+ /* Write into buffer */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, *n_data, n_data, mpi);
+ g_return_val_if_fail (gcry == 0, NULL);
+
+ return data;
+}
+
+static gcry_mpi_t
+mpi_from_data (const guchar *data,
+ gsize n_data)
+{
+ gcry_mpi_t mpi;
+ gcry_error_t gcry;
+
+ gcry = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, data, n_data, NULL);
+ return (gcry == 0) ? mpi : NULL;
+}
+
+static void
+gcr_secret_exchange_default_free (gpointer to_free)
+{
+ GcrSecretExchangeDefault *data = to_free;
+ gcry_mpi_release (data->prime);
+ gcry_mpi_release (data->base);
+ gcry_mpi_release (data->pub);
+ gcry_mpi_release (data->priv);
+ if (data->key) {
+ egg_secure_clear (data->key, EXCHANGE_1_KEY_LENGTH);
+ egg_secure_free (data->key);
+ }
+ g_free (data);
+}
+
+static gboolean
+gcr_secret_exchange_default_generate_exchange_key (GcrSecretExchange *exchange,
+ const gchar *scheme,
+ guchar **public_key,
+ gsize *n_public_key)
+{
+ GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
+
+ if (data == NULL) {
+ data = g_new0 (GcrSecretExchangeDefault, 1);
+ if (!egg_dh_default_params (EXCHANGE_1_IKE_NAME, &data->prime, &data->base))
+ g_return_val_if_reached (FALSE);
+
+ exchange->pv->default_exchange = data;
+ exchange->pv->destroy_exchange = gcr_secret_exchange_default_free;
+ }
+
+ gcry_mpi_release (data->priv);
+ data->priv = NULL;
+ gcry_mpi_release (data->pub);
+ data->pub = NULL;
+ egg_secure_free (data->key);
+ data->key = NULL;
+
+ if (!egg_dh_gen_pair (data->prime, data->base, 0,
+ &data->pub, &data->priv))
+ g_return_val_if_reached (FALSE);
+
+ *public_key = mpi_to_data (data->pub, n_public_key);
+ return *public_key != NULL;
+}
+
+static gboolean
+gcr_secret_exchange_default_derive_transport_key (GcrSecretExchange *exchange,
+ const guchar *peer,
+ gsize n_peer)
+{
+ GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
+ gpointer ikm;
+ gsize n_ikm;
+ gcry_mpi_t mpi;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (data->priv != NULL, FALSE);
+
+ mpi = mpi_from_data (peer, n_peer);
+ if (mpi == NULL)
+ return FALSE;
+
+ /* Build up a key we can use */
+ ikm = egg_dh_gen_secret (mpi, data->priv, data->prime, &n_ikm);
+ g_return_val_if_fail (ikm != NULL, FALSE);
+
+ if (data->key == NULL)
+ data->key = egg_secure_alloc (EXCHANGE_1_KEY_LENGTH);
+
+ if (!egg_hkdf_perform (EXCHANGE_1_HASH_ALGO, ikm, n_ikm, NULL, 0,
+ NULL, 0, data->key, EXCHANGE_1_KEY_LENGTH))
+ g_return_val_if_reached (FALSE);
+
+ egg_secure_free (ikm);
+ gcry_mpi_release (mpi);
+
+ return TRUE;
+}
+
+static gboolean
+gcr_secret_exchange_default_encrypt_transport_data (GcrSecretExchange *exchange,
+ GckAllocator allocator,
+ const guchar *plain_text,
+ gsize n_plain_text,
+ guchar **iv,
+ gsize *n_iv,
+ guchar **cipher_text,
+ gsize *n_cipher_text)
+{
+ GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
+ gcry_cipher_hd_t cih;
+ gcry_error_t gcry;
+ guchar *padded;
+ gsize n_result;
+ guchar *result;
+ gsize pos;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (data->key != 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));
+ g_free (iv);
+ return FALSE;
+ }
+
+ *iv = (allocator) (NULL, EXCHANGE_1_IV_LENGTH);
+ g_return_val_if_fail (*iv != NULL, FALSE);
+ gcry_create_nonce (*iv, EXCHANGE_1_IV_LENGTH);
+ *n_iv = EXCHANGE_1_IV_LENGTH;
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setkey (cih, data->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);
+
+ /* Pad the text properly */
+ if (!egg_padding_pkcs7_pad (egg_secure_realloc, 16, plain_text, n_plain_text,
+ (gpointer*)&padded, &n_result))
+ g_return_val_if_reached (FALSE);
+ result = (allocator) (NULL, n_result);
+ g_return_val_if_fail (result != NULL, FALSE);
+
+ 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);
+
+ *cipher_text = result;
+ *n_cipher_text = n_result;
+ return TRUE;
+}
+
+static gboolean
+gcr_secret_exchange_default_decrypt_transport_data (GcrSecretExchange *exchange,
+ GckAllocator allocator,
+ const guchar *cipher_text,
+ gsize n_cipher_text,
+ const guchar *iv,
+ gsize n_iv,
+ guchar **plain_text,
+ gsize *n_plain_text)
+{
+ GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
+ guchar* padded;
+ guchar* result;
+ gsize n_result;
+ gsize pos;
+ gcry_cipher_hd_t cih;
+ gcry_error_t gcry;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (data->key != NULL, FALSE);
+
+ if (iv == NULL || n_iv != EXCHANGE_1_IV_LENGTH) {
+ g_message ("secret-exchange: invalid or missing iv");
+ return FALSE;
+ }
+
+ if (n_cipher_text % 16 != 0) {
+ g_message ("secret-message: invalid length for cipher text");
+ return 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));
+ return FALSE;
+ }
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setkey (cih, data->key, EXCHANGE_1_KEY_LENGTH);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setiv (cih, iv, n_iv);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ /* Allocate memory for the result */
+ padded = (allocator) (NULL, n_cipher_text);
+ g_return_val_if_fail (padded != NULL, FALSE);
+
+ for (pos = 0; pos < n_cipher_text; pos += 16) {
+ gcry = gcry_cipher_decrypt (cih, padded + pos, 16, (guchar *)cipher_text + pos, 16);
+ g_return_val_if_fail (gcry == 0, FALSE);
+ }
+
+ gcry_cipher_close (cih);
+
+ if (!egg_padding_pkcs7_unpad (allocator, 16, padded, n_cipher_text,
+ (gpointer*)&result, &n_result))
+ result = NULL;
+
+ /* Free the padded text */
+ (allocator) (padded, 0);
+
+ *plain_text = result;
+ *n_plain_text = n_result;
+ return TRUE;
+}
+
+static void
+gcr_secret_exchange_class_init (GcrSecretExchangeClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = gcr_secret_exchange_get_property;
+ gobject_class->set_property = gcr_secret_exchange_set_property;
+ gobject_class->finalize = gcr_secret_exchange_finalize;
+
+ klass->generate_exchange_key = gcr_secret_exchange_default_generate_exchange_key;
+ klass->derive_transport_key = gcr_secret_exchange_default_derive_transport_key;
+ klass->decrypt_transport_data = gcr_secret_exchange_default_decrypt_transport_data;
+ klass->encrypt_transport_data = gcr_secret_exchange_default_encrypt_transport_data;
+
+ g_type_class_add_private (gobject_class, sizeof (GcrSecretExchangePrivate));
+
+ egg_libgcrypt_initialize ();
+
+ /**
+ * GcrSecretExchange:protocol:
+ *
+ * The protocol being used for the exchange.
+ *
+ * Will be %NULL if no protocol was specified when creating this object,
+ * and either gcr_secret_exchange_begin() or gcr_secret_exchange_receive()
+ * have not been called successfully.
+ */
+ g_object_class_install_property (gobject_class, PROP_PROTOCOL,
+ g_param_spec_string ("protocol", "Protocol", "Exchange protocol",
+ GCR_SECRET_EXCHANGE_PROTOCOL_1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/gcr/gcr-secret-exchange.h b/gcr/gcr-secret-exchange.h
index 41ff8b3..5608f63 100644
--- a/gcr/gcr-secret-exchange.h
+++ b/gcr/gcr-secret-exchange.h
@@ -24,12 +24,14 @@
#ifndef __GCR_SECRET_EXCHANGE_H__
#define __GCR_SECRET_EXCHANGE_H__
-#include "gcr.h"
+#include "gcr-base.h"
#include <glib-object.h>
G_BEGIN_DECLS
+#define GCR_SECRET_EXCHANGE_PROTOCOL_1 "sx-aes-1"
+
#define GCR_TYPE_SECRET_EXCHANGE (gcr_secret_exchange_get_type ())
#define GCR_SECRET_EXCHANGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_SECRET_EXCHANGE, GcrSecretExchange))
#define GCR_SECRET_EXCHANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_SECRET_EXCHANGE, GcrSecretExchangeClass))
@@ -50,13 +52,45 @@ struct _GcrSecretExchange {
struct _GcrSecretExchangeClass {
/*< private >*/
GObjectClass parent_class;
+
+ /* virtual methods, not used publicly */
+ gboolean (*generate_exchange_key) (GcrSecretExchange *exchange,
+ const gchar *scheme,
+ guchar **public_key,
+ gsize *n_public_key);
+
+ gboolean (*derive_transport_key) (GcrSecretExchange *exchange,
+ const guchar *peer,
+ gsize n_peer);
+
+ gboolean (*encrypt_transport_data) (GcrSecretExchange *exchange,
+ GckAllocator allocator,
+ const guchar *plain_text,
+ gsize n_plain_text,
+ guchar **parameter,
+ gsize *n_parameter,
+ guchar **cipher_text,
+ gsize *n_cipher_text);
+
+ gboolean (*decrypt_transport_data) (GcrSecretExchange *exchange,
+ GckAllocator allocator,
+ const guchar *cipher_text,
+ gsize n_cipher_text,
+ const guchar *parameter,
+ gsize n_parameter,
+ guchar **plain_text,
+ gsize *n_plain_text);
+
+ gpointer dummy[6];
};
/* Caller side functions */
GType gcr_secret_exchange_get_type (void);
-GcrSecretExchange * gcr_secret_exchange_new (void);
+GcrSecretExchange * gcr_secret_exchange_new (const gchar *protocol);
+
+const gchar * gcr_secret_exchange_get_protocol (GcrSecretExchange *self);
gchar * gcr_secret_exchange_begin (GcrSecretExchange *self);
diff --git a/gcr/tests/test-secret-exchange.c b/gcr/tests/test-secret-exchange.c
index 48581fe..7964745 100644
--- a/gcr/tests/test-secret-exchange.c
+++ b/gcr/tests/test-secret-exchange.c
@@ -36,9 +36,9 @@ typedef struct {
static void
setup (Test *test, gconstpointer unused)
{
- test->caller = gcr_secret_exchange_new ();
+ test->caller = gcr_secret_exchange_new (NULL);
g_assert (GCR_IS_SECRET_EXCHANGE (test->caller));
- test->callee = gcr_secret_exchange_new ();
+ test->callee = gcr_secret_exchange_new (NULL);
g_assert (GCR_IS_SECRET_EXCHANGE (test->callee));
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]