From: Stef Walter Date: Sat, 20 Aug 2011 20:50:42 +0000 (+0200) Subject: gcr: Refactor GcrSecretExchange X-Git-Tag: split~21 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e09c71b6fcdc25800d684f52c3c853a1069f2cef;p=platform%2Fupstream%2Fgcr.git gcr: Refactor GcrSecretExchange * Use the GcrSecretExchange object on both sides. * Allow exchange of multiple secrets, and in both directions. * Add tests --- diff --git a/gcr/gcr-base.h b/gcr/gcr-base.h index 54cc629..69133f1 100644 --- a/gcr/gcr-base.h +++ b/gcr/gcr-base.h @@ -43,7 +43,9 @@ #include "gcr-library.h" #include "gcr-parser.h" #include "gcr-pkcs11-certificate.h" +#include "gcr-secret-exchange.h" #include "gcr-simple-certificate.h" +#include "gcr-simple-collection.h" #include "gcr-trust.h" #include "gcr-union-collection.h" #include "gcr-unlock-options.h" diff --git a/gcr/gcr-secret-exchange.c b/gcr/gcr-secret-exchange.c index 768083c..ff5d3e6 100644 --- a/gcr/gcr-secret-exchange.c +++ b/gcr/gcr-secret-exchange.c @@ -52,8 +52,10 @@ struct _GcrSecretExchangePrivate { gcry_mpi_t prime; gcry_mpi_t base; + gcry_mpi_t pub; gcry_mpi_t priv; - guchar *secret; + gpointer key; + gchar *secret; gsize n_secret; }; @@ -134,10 +136,6 @@ key_file_get_mpi (GKeyFile *key_file, const gchar *section, return (gcry == 0) ? mpi : NULL; } -/* ---------------------------------------------------------------------------- - * REQUESTER SIDE - */ - static void gcr_secret_exchange_init (GcrSecretExchange *self) { @@ -151,10 +149,12 @@ gcr_secret_exchange_init (GcrSecretExchange *self) static void clear_secret_exchange (GcrSecretExchange *self) { - if (self->pv->priv) { - gcry_mpi_release (self->pv->priv); - self->pv->priv = NULL; - } + 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; egg_secure_free (self->pv->secret); self->pv->secret = NULL; self->pv->n_secret = 0; @@ -166,7 +166,8 @@ gcr_secret_exchange_finalize (GObject *obj) GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj); clear_secret_exchange (self); - gcry_mpi_release (self->pv->priv); + gcry_mpi_release (self->pv->prime); + gcry_mpi_release (self->pv->base); G_OBJECT_CLASS (gcr_secret_exchange_parent_class)->finalize (obj); } @@ -189,23 +190,23 @@ gcr_secret_exchange_new (void) } gchar * -gcr_secret_exchange_request (GcrSecretExchange *self) +gcr_secret_exchange_begin (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); + g_assert (self->pv->priv == NULL); output = g_key_file_new (); - if (!egg_dh_gen_pair (self->pv->prime, self->pv->base, 0, &pub, &self->pv->priv)) + if (!egg_dh_gen_pair (self->pv->prime, self->pv->base, 0, + &self->pv->pub, &self->pv->priv)) g_return_val_if_reached (NULL); - key_file_set_mpi (output, EXCHANGE_VERSION, "public", pub); - gcry_mpi_release (pub); + key_file_set_mpi (output, EXCHANGE_VERSION, "public", self->pv->pub); result = g_key_file_to_data (output, NULL, NULL); g_return_val_if_fail (result != NULL, NULL); @@ -215,46 +216,46 @@ gcr_secret_exchange_request (GcrSecretExchange *self) return result; } -static gpointer -calculate_receive_key (GKeyFile *input, gcry_mpi_t prime, gcry_mpi_t priv) +static gboolean +calculate_key (GcrSecretExchange *self, + GKeyFile *input) { 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; + return FALSE; } /* 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); + 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); - 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); + NULL, 0, self->pv->key, EXCHANGE_1_KEY_LENGTH)) + g_return_val_if_reached (FALSE); egg_secure_free (ikm); gcry_mpi_release (peer); - return key; + return TRUE; } static gpointer -perform_aes_decrypt (GKeyFile *input, - gcry_mpi_t prime, - gcry_mpi_t priv, +perform_aes_decrypt (GcrSecretExchange *self, + GKeyFile *input, 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; @@ -268,37 +269,28 @@ perform_aes_decrypt (GKeyFile *input, return NULL; } - value = key_file_get_base64 (input, EXCHANGE_VERSION, "value", &n_value); + value = key_file_get_base64 (input, EXCHANGE_VERSION, "secret", &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); + 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); - egg_secure_free (key); g_free (iv); /* Allocate memory for the result */ @@ -324,36 +316,50 @@ perform_aes_decrypt (GKeyFile *input, gboolean gcr_secret_exchange_receive (GcrSecretExchange *self, - const gchar *response) + const gchar *exchange) { GKeyFile *input; - guchar *secret; + gchar *secret; gsize n_secret; + gboolean ret; /* Parse the input */ input = g_key_file_new (); - if (!g_key_file_load_from_data (input, response, strlen (response), + if (!g_key_file_load_from_data (input, exchange, strlen (exchange), G_KEY_FILE_NONE, NULL)) { g_key_file_free (input); - g_message ("couldn't parse secret exchange request data"); + g_message ("couldn't parse secret exchange data"); return FALSE; } - secret = perform_aes_decrypt (input, self->pv->prime, self->pv->priv, &n_secret); - g_key_file_free (input); + if (self->pv->priv == NULL) { + if (!egg_dh_gen_pair (self->pv->prime, self->pv->base, 0, + &self->pv->pub, &self->pv->priv)) + g_return_val_if_reached (FALSE); + } + + if (!calculate_key (self, input)) + return FALSE; - if (secret != NULL) { - egg_secure_free (self->pv->secret); - self->pv->secret = secret; - self->pv->n_secret = n_secret; + ret = TRUE; + + if (g_key_file_has_key (input, EXCHANGE_VERSION, "secret", NULL)) { + secret = perform_aes_decrypt (self, input, &n_secret); + if (secret == NULL) { + ret = FALSE; + } else { + egg_secure_free (self->pv->secret); + self->pv->secret = secret; + self->pv->n_secret = n_secret; + } } - return (secret != NULL); + return ret; } -const guchar * -gcr_secret_exchange_get_response (GcrSecretExchange *self, - gsize *secret_len) +const gchar * +gcr_secret_exchange_get_secret (GcrSecretExchange *self, + gsize *secret_len) { g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL); @@ -362,58 +368,8 @@ gcr_secret_exchange_get_response (GcrSecretExchange *self, 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) +calculate_iv (GKeyFile *output) { gpointer iv; @@ -425,29 +381,25 @@ calculate_response_iv (GKeyFile *input, GKeyFile *output) } static gboolean -perform_aes_encrypt (GKeyFile *input, GKeyFile *output, - gconstpointer secret, gsize n_secret) +perform_aes_encrypt (GKeyFile *output, + gconstpointer key, + const gchar *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); + iv = calculate_iv (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; } @@ -460,7 +412,6 @@ perform_aes_encrypt (GKeyFile *input, GKeyFile *output, 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 */ @@ -479,45 +430,42 @@ perform_aes_encrypt (GKeyFile *input, GKeyFile *output, egg_secure_clear (padded, n_result); egg_secure_free (padded); - key_file_set_base64 (output, EXCHANGE_VERSION, "value", result, n_result); + key_file_set_base64 (output, EXCHANGE_VERSION, "secret", result, n_result); g_free (result); return TRUE; } gchar * -gcr_secret_exchange_respond (const gchar *request, - const guchar *secret, - gssize secret_len) +gcr_secret_exchange_send (GcrSecretExchange *self, + const gchar *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); + g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL); - /* 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"); + if (self->pv->key == NULL) { + 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); - 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); + if (secret != NULL) { + if (secret_len < 0) + secret_len = strlen (secret); + if (!perform_aes_encrypt (output, self->pv->key, secret, secret_len)) { + g_key_file_free (output); + return NULL; + } } - g_key_file_free (input); + result = g_key_file_to_data (output, NULL, NULL); + g_return_val_if_fail (result != NULL, NULL); g_key_file_free (output); - return result; } diff --git a/gcr/gcr-secret-exchange.h b/gcr/gcr-secret-exchange.h index 8ec8264..5bd3ecf 100644 --- a/gcr/gcr-secret-exchange.h +++ b/gcr/gcr-secret-exchange.h @@ -31,11 +31,11 @@ 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)) +#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)) +#define GCR_IS_SECRET_EXCHANGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_SECRET_EXCHANGE)) +#define GCR_IS_SECRET_EXCHANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_SECRET_EXCHANGE)) +#define GCR_SECRET_EXCHANGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_SECRET_EXCHANGE, GcrSecretExchangeClass)) typedef struct _GcrSecretExchange GcrSecretExchange; typedef struct _GcrSecretExchangeClass GcrSecretExchangeClass; @@ -58,20 +58,17 @@ GType gcr_secret_exchange_get_type (void); GcrSecretExchange * gcr_secret_exchange_new (void); -gchar * gcr_secret_exchange_request (GcrSecretExchange *self); +gchar * gcr_secret_exchange_begin (GcrSecretExchange *self); gboolean gcr_secret_exchange_receive (GcrSecretExchange *self, - const gchar *response); + const gchar *exchange); -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, +gchar * gcr_secret_exchange_send (GcrSecretExchange *self, + const gchar *secret, gssize secret_len); +const gchar * gcr_secret_exchange_get_secret (GcrSecretExchange *self, + gsize *secret_len); G_END_DECLS diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am index 018f0bb..5872ff9 100644 --- a/gcr/tests/Makefile.am +++ b/gcr/tests/Makefile.am @@ -21,6 +21,7 @@ LDADD = \ TEST_PROGS = \ test-util \ + test-secret-exchange \ test-simple-certificate \ test-certificate \ test-certificate-chain \ diff --git a/gcr/tests/test-secret-exchange.c b/gcr/tests/test-secret-exchange.c new file mode 100644 index 0000000..48581fe --- /dev/null +++ b/gcr/tests/test-secret-exchange.c @@ -0,0 +1,162 @@ +/* -*- 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 +*/ + +#include "config.h" + +#include "gcr/gcr.h" + +#include + +#include + +typedef struct { + GcrSecretExchange *caller; + GcrSecretExchange *callee; +} Test; + +static void +setup (Test *test, gconstpointer unused) +{ + test->caller = gcr_secret_exchange_new (); + g_assert (GCR_IS_SECRET_EXCHANGE (test->caller)); + test->callee = gcr_secret_exchange_new (); + g_assert (GCR_IS_SECRET_EXCHANGE (test->callee)); +} + +static void +teardown (Test *test, + gconstpointer unused) +{ + g_object_unref (test->caller); + g_assert (!GCR_IS_SECRET_EXCHANGE (test->caller)); + g_object_unref (test->callee); + g_assert (!GCR_IS_SECRET_EXCHANGE (test->callee)); +} + +static void +test_perform_exchange (Test *test, + gconstpointer unused) +{ + gchar *exchange; + + exchange = gcr_secret_exchange_begin (test->caller); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->callee, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + exchange = gcr_secret_exchange_send (test->callee, "the secret", -1); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->caller, exchange)) + g_assert_not_reached (); + + g_assert_cmpstr (gcr_secret_exchange_get_secret (test->caller, NULL), ==, "the secret"); + + g_free (exchange); +} + +static void +test_perform_reverse (Test *test, + gconstpointer unused) +{ + gchar *exchange; + + exchange = gcr_secret_exchange_begin (test->caller); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->callee, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + exchange = gcr_secret_exchange_send (test->callee, NULL, -1); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->caller, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + g_assert (gcr_secret_exchange_get_secret (test->caller, NULL) == NULL); + + exchange = gcr_secret_exchange_send (test->caller, "reverse secret", -1); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->callee, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + g_assert_cmpstr (gcr_secret_exchange_get_secret (test->callee, NULL), ==, "reverse secret"); +} + +static void +test_perform_multiple (Test *test, + gconstpointer unused) +{ + gchar *exchange; + + exchange = gcr_secret_exchange_begin (test->caller); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->callee, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + exchange = gcr_secret_exchange_send (test->callee, "first secret", -1); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->caller, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + g_assert_cmpstr (gcr_secret_exchange_get_secret (test->caller, NULL), ==, "first secret"); + + exchange = gcr_secret_exchange_send (test->callee, "second secret", -1); + g_assert (exchange); + + if (!gcr_secret_exchange_receive (test->caller, exchange)) + g_assert_not_reached (); + + g_free (exchange); + + g_assert_cmpstr (gcr_secret_exchange_get_secret (test->caller, NULL), ==, "second secret"); +} + +int +main (int argc, char **argv) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-secret-exchange"); + + g_test_add ("/gcr/secret-exchange/perform-exchange", Test, NULL, setup, test_perform_exchange, teardown); + g_test_add ("/gcr/secret-exchange/perform-reverse", Test, NULL, setup, test_perform_reverse, teardown); + g_test_add ("/gcr/secret-exchange/perform-multiple", Test, NULL, setup, test_perform_multiple, teardown); + + return g_test_run (); +}