changeset 29104:1c1910b17ae5

jabber: Clean up the SCRAM code a little.
author Paul Aurich <paul@darkrain42.org>
date Tue, 01 Dec 2009 02:53:29 +0000
parents f10741a709a9
children 7a78b609786c
files libpurple/protocols/jabber/auth_scram.c libpurple/protocols/jabber/auth_scram.h libpurple/tests/test_jabber_scram.c
diffstat 3 files changed, 49 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/auth_scram.c	Tue Dec 01 00:30:22 2009 +0000
+++ b/libpurple/protocols/jabber/auth_scram.c	Tue Dec 01 02:53:29 2009 +0000
@@ -28,59 +28,31 @@
 #include "cipher.h"
 #include "debug.h"
 
-static const struct {
-	const char *mech_substr;
-	const char *hash;
-} mech_hashes[] = {
-	{ "-SHA-1", "sha1" },
+static const JabberScramHash hashes[] = {
+	{ "-SHA-1", "sha1", 20 },
 };
 
-static const struct {
-	const char *hash;
-	guint size;
-} hash_sizes[] = {
-	{ "sha1", 20 },
-};
-
-static const gchar *mech_to_hash(const char *mech)
+static const JabberScramHash *mech_to_hash(const char *mech)
 {
 	int i;
 
 	g_return_val_if_fail(mech != NULL && *mech != '\0', NULL);
 
-	for (i = 0; i < G_N_ELEMENTS(mech_hashes); ++i) {
-		if (strstr(mech, mech_hashes[i].mech_substr))
-			return mech_hashes[i].hash;
+	for (i = 0; i < G_N_ELEMENTS(hashes); ++i) {
+		if (strstr(mech, hashes[i].mech_substr))
+			return &(hashes[i]);
 	}
 
 	purple_debug_error("jabber", "Unknown SCRAM mechanism %s\n", mech);
-
-	return NULL;
+	g_return_val_if_reached(NULL);
 }
 
-static guint hash_to_output_len(const gchar *hash)
-{
-	int i;
-
-	g_return_val_if_fail(hash != NULL && *hash != '\0', 0);
-
-	for (i = 0; i < G_N_ELEMENTS(hash_sizes); ++i) {
-		if (g_str_equal(hash, hash_sizes[i].hash))
-			return hash_sizes[i].size;
-	}
-
-	purple_debug_error("jabber", "Unknown SCRAM hash function %s\n", hash);
-
-	return 0;
-}
-
-guchar *jabber_scram_hi(const gchar *hash, const GString *str,
+guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
                         GString *salt, guint iterations)
 {
 	PurpleCipherContext *context;
 	guchar *result;
 	guint i;
-	guint hash_len;
 	guchar *prev, *tmp;
 
 	g_return_val_if_fail(hash != NULL, NULL);
@@ -88,12 +60,9 @@
 	g_return_val_if_fail(salt != NULL && salt->len > 0, NULL);
 	g_return_val_if_fail(iterations > 0, NULL);
 
-	hash_len = hash_to_output_len(hash);
-	g_return_val_if_fail(hash_len > 0, NULL);
-
-	prev = g_new0(guint8, hash_len);
-	tmp = g_new0(guint8, hash_len);
-	result = g_new0(guint8, hash_len);
+	prev   = g_new0(guint8, hash->size);
+	tmp    = g_new0(guint8, hash->size);
+	result = g_new0(guint8, hash->size);
 
 	context = purple_cipher_context_new_by_name("hmac", NULL);
 
@@ -102,25 +71,25 @@
 	g_string_append_len(salt, "\0\0\0\1", 4);
 
 	/* Compute U0 */
-	purple_cipher_context_set_option(context, "hash", (gpointer)hash);
+	purple_cipher_context_set_option(context, "hash", (gpointer)hash->name);
 	purple_cipher_context_set_key_with_len(context, (guchar *)str->str, str->len);
 	purple_cipher_context_append(context, (guchar *)salt->str, salt->len);
-	purple_cipher_context_digest(context, hash_len, result, NULL);
+	purple_cipher_context_digest(context, hash->size, result, NULL);
 
-	memcpy(prev, result, hash_len);
+	memcpy(prev, result, hash->size);
 
 	/* Compute U1...Ui */
 	for (i = 1; i < iterations; ++i) {
 		guint j;
-		purple_cipher_context_set_option(context, "hash", (gpointer)hash);
+		purple_cipher_context_set_option(context, "hash", (gpointer)hash->name);
 		purple_cipher_context_set_key_with_len(context, (guchar *)str->str, str->len);
-		purple_cipher_context_append(context, prev, hash_len);
-		purple_cipher_context_digest(context, hash_len, tmp, NULL);
+		purple_cipher_context_append(context, prev, hash->size);
+		purple_cipher_context_digest(context, hash->size, tmp, NULL);
 
-		for (j = 0; j < hash_len; ++j)
+		for (j = 0; j < hash->size; ++j)
 			result[j] ^= tmp[j];
 
-		memcpy(prev, tmp, hash_len);
+		memcpy(prev, tmp, hash->size);
 	}
 
 	purple_cipher_context_destroy(context);
@@ -131,40 +100,41 @@
 
 /*
  * Helper functions for doing the SCRAM calculations. The first argument
- * is the hash algorithm and the second (len) is the length of the output
- * buffer and key/data (the fourth argument).
+ * is the hash algorithm.  All buffers must be of the appropriate size
+ * according to the JabberScramHash.
+ *
  * "str" is a NULL-terminated string for hmac().
  *
  * Needless to say, these are fragile.
  */
 static void
-hmac(const gchar *hash_alg, gsize len, guchar *out, const guchar *key, const gchar *str)
+hmac(const JabberScramHash *hash, guchar *out, const guchar *key, const gchar *str)
 {
 	PurpleCipherContext *context;
 
 	context = purple_cipher_context_new_by_name("hmac", NULL);
-	purple_cipher_context_set_option(context, "hash", (gpointer)hash_alg);
-	purple_cipher_context_set_key_with_len(context, key, len);
+	purple_cipher_context_set_option(context, "hash", (gpointer)hash->name);
+	purple_cipher_context_set_key_with_len(context, key, hash->size);
 	purple_cipher_context_append(context, (guchar *)str, strlen(str));
-	purple_cipher_context_digest(context, len, out, NULL);
+	purple_cipher_context_digest(context, hash->size, out, NULL);
 	purple_cipher_context_destroy(context);
 }
 
 static void
-hash(const gchar *hash_alg, gsize len, guchar *out, const guchar *data)
+hash(const JabberScramHash *hash, guchar *out, const guchar *data)
 {
 	PurpleCipherContext *context;
 
-	context = purple_cipher_context_new_by_name(hash_alg, NULL);
-	purple_cipher_context_append(context, data, len);
-	purple_cipher_context_digest(context, len, out, NULL);
+	context = purple_cipher_context_new_by_name(hash->name, NULL);
+	purple_cipher_context_append(context, data, hash->size);
+	purple_cipher_context_digest(context, hash->size, out, NULL);
 	purple_cipher_context_destroy(context);
 }
 
 gboolean
 jabber_scram_calc_proofs(JabberScramData *data, GString *salt, guint iterations)
 {
-	guint hash_len = hash_to_output_len(data->hash);
+	guint hash_len = data->hash->size;
 	guint i;
 
 	GString *pass = g_string_new(data->password);
@@ -189,18 +159,18 @@
 		return FALSE;
 
 	/* client_key = HMAC(salted_password, "Client Key") */
-	hmac(data->hash, hash_len, client_key, salted_password, "Client Key");
+	hmac(data->hash, client_key, salted_password, "Client Key");
 	/* server_key = HMAC(salted_password, "Server Key") */
-	hmac(data->hash, hash_len, server_key, salted_password, "Server Key");
+	hmac(data->hash, server_key, salted_password, "Server Key");
 	g_free(salted_password);
 
 	/* stored_key = HASH(client_key) */
-	hash(data->hash, hash_len, stored_key, client_key);
+	hash(data->hash, stored_key, client_key);
 
 	/* client_signature = HMAC(stored_key, auth_message) */
-	hmac(data->hash, hash_len, client_signature, stored_key, data->auth_message->str);
+	hmac(data->hash, client_signature, stored_key, data->auth_message->str);
 	/* server_signature = HMAC(server_key, auth_message) */
-	hmac(data->hash, hash_len, (guchar *)data->server_signature->str, server_key, data->auth_message->str);
+	hmac(data->hash, (guchar *)data->server_signature->str, server_key, data->auth_message->str);
 
 	/* client_proof = client_key XOR client_signature */
 	for (i = 0; i < hash_len; ++i)
@@ -570,12 +540,6 @@
 };
 #endif
 
-/* For tests */
-JabberSaslMech *jabber_scram_get_sha1(void)
-{
-	return &scram_sha1_mech;
-}
-
 JabberSaslMech **jabber_auth_get_scram_mechs(gint *count)
 {
 	static JabberSaslMech *mechs[] = {
@@ -585,8 +549,6 @@
 #endif
 	};
 
-	g_return_val_if_fail(count != NULL, NULL);
-
 	*count = G_N_ELEMENTS(mechs);
 	return mechs;
 }
--- a/libpurple/protocols/jabber/auth_scram.h	Tue Dec 01 00:30:22 2009 +0000
+++ b/libpurple/protocols/jabber/auth_scram.h	Tue Dec 01 02:53:29 2009 +0000
@@ -32,9 +32,14 @@
 /* Per-connection state stored between messages.
  * This is stored in js->auth_data_mech.
  */
+typedef struct {
+	const char *mech_substr;
+	const char *name;
+	guint size;
+} JabberScramHash;
 
 typedef struct {
-	const char *hash;
+	const JabberScramHash *hash;
 	char *cnonce;
 	GString *auth_message;
 
@@ -48,14 +53,10 @@
 
 #include "auth.h"
 
-JabberSaslMech *jabber_scram_get_sha1(void);
-
 /**
  * Implements the Hi() function as described in the SASL-SCRAM I-D.
  *
- * @param hash The name of a hash function to be used with HMAC.  This should
- *             be suitable to be passed to the libpurple cipher API.  Typically
- *             it will be "sha1".
+ * @param hash The struct corresponding to the hash function to be used.
  * @param str  The string to perform the PBKDF2 operation on.
  * @param salt The salt.
  * @param iterations The number of iterations to perform.
@@ -64,7 +65,7 @@
  *          NOT null-terminated and its length is the length of the binary
  *          output of the hash function in-use.
  */
-guchar *jabber_scram_hi(const char *hash, const GString *str,
+guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str,
                         GString *salt, guint iterations);
 
 /**
--- a/libpurple/tests/test_jabber_scram.c	Tue Dec 01 00:30:22 2009 +0000
+++ b/libpurple/tests/test_jabber_scram.c	Tue Dec 01 02:53:29 2009 +0000
@@ -4,10 +4,12 @@
 #include "../util.h"
 #include "../protocols/jabber/auth_scram.h"
 
+static JabberScramHash sha1_mech = { "-SHA-1", "sha1", 20 };
+
 #define assert_pbkdf2_equal(password, salt, count, expected) { \
 	GString *p = g_string_new(password); \
 	GString *s = g_string_new(salt); \
-	guchar *result = jabber_scram_hi("sha1", p, s, count); \
+	guchar *result = jabber_scram_hi(&sha1_mech, p, s, count); \
 	fail_if(result == NULL, "Hi() returned NULL"); \
 	fail_if(0 != memcmp(result, expected, 20), "Hi() returned invalid result"); \
 	g_string_free(s, TRUE); \
@@ -35,7 +37,7 @@
 	const char *client_proof;
 /*	const char *server_signature; */
 
-	data->hash = "sha1";
+	data->hash = &sha1_mech;
 	data->password = g_strdup("password");
 	data->auth_message = g_string_new("n=username@jabber.org,r=8jLxB5515dhFxBil5A0xSXMH,"
 			"r=8jLxB5515dhFxBil5A0xSXMHabc,s=c2FsdA==,i=1,"
@@ -60,7 +62,7 @@
 	gchar *out;
 
 	data->step = 1;
-	data->hash = "sha1";
+	data->hash = &sha1_mech;
 	data->password = g_strdup("password");
 	data->cnonce = g_strdup("H7yDYKAWBCrM2Fa5SxGa4iez");
 	data->auth_message = g_string_new("n=paul,r=H7yDYKAWBCrM2Fa5SxGa4iez");