cpw.darkrain42.xmpp.scram: 92bbea97: jabber: Complete (though untested) SCRAM...
darkrain42 at pidgin.im
darkrain42 at pidgin.im
Sun Nov 8 23:46:21 EST 2009
-----------------------------------------------------------------
Revision: 92bbea97933c3e466360e44fee512fe7e12e6407
Ancestor: 4f47d6ab05315c39e7b1133c7e4e4129c781d46f
Author: darkrain42 at pidgin.im
Date: 2009-11-09T03:42:26
Branch: im.pidgin.cpw.darkrain42.xmpp.scram
URL: http://d.pidgin.im/viewmtn/revision/info/92bbea97933c3e466360e44fee512fe7e12e6407
Modified files:
libpurple/protocols/jabber/auth.c
libpurple/protocols/jabber/auth.h
libpurple/protocols/jabber/auth_scram.c
libpurple/protocols/jabber/auth_scram.h
libpurple/protocols/jabber/jabber.c
libpurple/protocols/jabber/jabber.h
libpurple/tests/test_jabber_scram.c
ChangeLog:
jabber: Complete (though untested) SCRAM implementation.
Client proof calculations function properly, but parsing is untested.
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/auth.c be4b028440f5d3d6a69a805e5df5338521de010e
+++ libpurple/protocols/jabber/auth.c 186e224cd251d59ac1c1d6d98c45595b1b17ca0e
@@ -485,11 +485,18 @@ void jabber_auth_init(void)
void jabber_auth_init(void)
{
+ JabberSaslMech **tmp;
+ gint count, i;
+
auth_mechs = g_slist_insert_sorted(auth_mechs, jabber_auth_get_plain_mech(), compare_mech);
auth_mechs = g_slist_insert_sorted(auth_mechs, jabber_auth_get_digest_md5_mech(), compare_mech);
#ifdef HAVE_CYRUS_SASL
auth_mechs = g_slist_insert_sorted(auth_mechs, jabber_auth_get_cyrus_mech(), compare_mech);
#endif
+
+ tmp = jabber_auth_get_scram_mechs(&count);
+ for (i = 0; i < count; ++i)
+ auth_mechs = g_slist_insert_sorted(auth_mechs, tmp[i], compare_mech);
}
void jabber_auth_uninit(void)
============================================================
--- libpurple/protocols/jabber/auth.h f2caea0def47f7be781c6c70ed5d98c0f257bbab
+++ libpurple/protocols/jabber/auth.h 5c420ab419bd1cd5f25ad5e609b280c3a063f838
@@ -48,6 +48,7 @@ JabberSaslMech *jabber_auth_get_digest_m
JabberSaslMech *jabber_auth_get_plain_mech(void);
JabberSaslMech *jabber_auth_get_digest_md5_mech(void);
+JabberSaslMech **jabber_auth_get_scram_mechs(gint *count);
#ifdef HAVE_CYRUS_SASL
JabberSaslMech *jabber_auth_get_cyrus_mech(void);
#endif
============================================================
--- libpurple/protocols/jabber/auth_scram.c c928bd8110416f7a64cca9f3137d00d737364471
+++ libpurple/protocols/jabber/auth_scram.c 864bcebf6ada20e0ed5419a9bf07c296ea115153
@@ -29,16 +29,33 @@ static const struct {
#include "debug.h"
static const struct {
+ const char *mech_substr;
const char *hash;
+} mech_hashes[] = {
+ { "-SHA-1-", "sha1" },
+};
+
+static const struct {
+ const char *hash;
guint size;
} hash_sizes[] = {
{ "sha1", 20 },
- { "sha224", 28 },
- { "sha256", 32 },
- { "sha384", 48 },
- { "sha512", 64 }
};
+static const gchar *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;
+ }
+
+ return NULL;
+}
+
static guint hash_to_output_len(const gchar *hash)
{
int i;
@@ -55,11 +72,11 @@ static guint hash_to_output_len(const gc
return 0;
}
-GString *jabber_auth_scram_hi(const gchar *hash, const GString *str,
- GString *salt, guint iterations)
+guchar *jabber_scram_hi(const gchar *hash, const GString *str,
+ GString *salt, guint iterations)
{
PurpleCipherContext *context;
- GString *result;
+ guchar *result;
guint i;
guint hash_len;
guchar *prev, *tmp;
@@ -74,6 +91,7 @@ GString *jabber_auth_scram_hi(const gcha
prev = g_new0(guint8, hash_len);
tmp = g_new0(guint8, hash_len);
+ result = g_new0(guint8, hash_len);
context = purple_cipher_context_new_by_name("hmac", NULL);
@@ -81,15 +99,13 @@ GString *jabber_auth_scram_hi(const gcha
* octet first. */
g_string_append_len(salt, "\0\0\0\1", 4);
- result = g_string_sized_new(hash_len);
-
/* Compute U0 */
purple_cipher_context_set_option(context, "hash", (gpointer)hash);
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, (guchar *)result->str, &(result->len));
+ purple_cipher_context_digest(context, hash_len, result, NULL);
- memcpy(prev, result->str, hash_len);
+ memcpy(prev, result, hash_len);
/* Compute U1...Ui */
for (i = 1; i < iterations; ++i) {
@@ -100,7 +116,7 @@ GString *jabber_auth_scram_hi(const gcha
purple_cipher_context_digest(context, hash_len, tmp, NULL);
for (j = 0; j < hash_len; ++j)
- result->str[j] ^= tmp[j];
+ result[j] ^= tmp[j];
memcpy(prev, tmp, hash_len);
}
@@ -110,3 +126,431 @@ GString *jabber_auth_scram_hi(const gcha
g_free(prev);
return result;
}
+
+/*
+ * 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).
+ * "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)
+{
+ 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_append(context, (guchar *)str, strlen(str));
+ purple_cipher_context_digest(context, len, out, NULL);
+ purple_cipher_context_destroy(context);
+}
+
+static void
+hash(const gchar *hash_alg, gsize len, 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);
+ purple_cipher_context_destroy(context);
+}
+
+gboolean
+jabber_scram_calc_proofs(JabberScramData *data, const char *password,
+ GString *salt, guint iterations)
+{
+ guint hash_len = hash_to_output_len(data->hash);
+ guint i;
+
+ GString *pass = g_string_new(password);
+
+ guchar *salted_password;
+ guchar client_key[hash_len];
+ guchar stored_key[hash_len];
+ guchar client_signature[hash_len];
+ guchar server_key[hash_len];
+
+ data->client_proof = g_string_sized_new(hash_len);
+ data->client_proof->len = hash_len;
+ data->server_signature = g_string_sized_new(hash_len);
+ data->server_signature->len = hash_len;
+
+ salted_password = jabber_scram_hi(data->hash, pass, salt, iterations);
+ g_string_free(pass, TRUE);
+ if (!salted_password)
+ return FALSE;
+
+ /* client_key = HMAC(salted_password, "Client Key") */
+ hmac(data->hash, hash_len, client_key, salted_password, "Client Key");
+ /* server_key = HMAC(salted_password, "Server Key") */
+ hmac(data->hash, hash_len, server_key, salted_password, "Server Key");
+ g_free(salted_password);
+
+ /* stored_key = HASH(client_key) */
+ hash(data->hash, hash_len, stored_key, client_key);
+
+ /* client_signature = HMAC(stored_key, auth_message) */
+ hmac(data->hash, hash_len, 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);
+
+ /* client_proof = client_key XOR client_signature */
+ for (i = 0; i < hash_len; ++i)
+ data->client_proof->str[i] = client_key[i] ^ client_signature[i];
+
+ return TRUE;
+}
+
+static gboolean
+parse_challenge(JabberScramData *data, const char *challenge,
+ gchar **out_nonce, GString **out_salt, guint *out_iterations)
+{
+ gsize cnonce_len;
+ const char *cur;
+ const char *end;
+ const char *val_start, *val_end;
+ char *tmp, *decoded;
+ gsize decoded_len;
+ char *nonce;
+ GString *salt;
+ guint iterations;
+
+ cur = challenge;
+ end = challenge + strlen(challenge);
+
+ if (cur[0] != 'r' || cur[1] != '=')
+ return FALSE;
+
+ val_start = cur + 2;
+ val_end = strchr(val_start, ',');
+ if (val_end == NULL)
+ return FALSE;
+
+ /* Ensure that the first cnonce_len bytes of the nonce are the original
+ * cnonce we sent to the server.
+ */
+ cnonce_len = strlen(data->cnonce);
+ if ((val_end - val_start + 1) <= cnonce_len ||
+ strncmp(data->cnonce, val_start, cnonce_len) != 0)
+ return FALSE;
+
+ nonce = g_strndup(val_start, val_end - val_start + 1);
+
+ /* The Salt, base64-encoded */
+ cur = val_end + 1;
+ if (cur[0] != 's' || cur[1] != '=') {
+ g_free(nonce);
+ return FALSE;
+ }
+
+ val_start = cur + 2;
+ val_end = strchr(val_start, ',');
+ if (val_end == NULL) {
+ g_free(nonce);
+ return FALSE;
+ }
+
+ tmp = g_strndup(val_start, val_end - val_start + 1);
+ decoded = (gchar *)purple_base64_decode(tmp, &decoded_len);
+ g_free(tmp);
+ salt = g_string_new_len(decoded, decoded_len);
+ g_free(decoded);
+
+ /* The iteration count */
+ cur = val_end + 1;
+ if (cur[0] != 'i' || cur[1] != '=') {
+ g_free(nonce);
+ g_string_free(salt, TRUE);
+ return FALSE;
+ }
+
+ val_start = cur + 2;
+ val_end = strchr(val_start, ',');
+ if (val_end == NULL)
+ /* There could be extensions. This should possibly be a hard fail. */
+ val_end = end - 1;
+
+ /* Validate the string */
+ for (tmp = (gchar *)val_start; tmp != val_end; ++tmp) {
+ if (!g_ascii_isdigit(*tmp)) {
+ g_free(nonce);
+ g_string_free(salt, TRUE);
+ return FALSE;
+ }
+ }
+
+ tmp = g_strndup(val_start, val_end - val_start + 1);
+ iterations = strtoul(tmp, NULL, 10);
+ g_free(tmp);
+
+ *out_nonce = nonce;
+ *out_salt = salt;
+ *out_iterations = iterations;
+ return TRUE;
+}
+
+static gboolean
+parse_success(JabberScramData *data, const char *success,
+ gchar **out_verifier)
+{
+ const char *cur;
+ const char *val_start, *val_end;
+ const char *end;
+
+ char *verifier;
+
+ g_return_val_if_fail(data != NULL, FALSE);
+ g_return_val_if_fail(success != NULL, FALSE);
+ g_return_val_if_fail(out_verifier != NULL, FALSE);
+
+ cur = success;
+ end = cur + strlen(cur);
+
+ if (cur[0] != 'v' || cur[1] != '=') {
+ /* TODO: Error handling */
+ return FALSE;
+ }
+
+ val_start = cur + 2;
+ val_end = strchr(val_start, ',');
+ if (val_end == NULL)
+ /* TODO: Maybe make this a strict check on not having any extensions? */
+ val_end = end - 1;
+
+ verifier = g_strndup(val_start, val_end - val_start + 1);
+
+ *out_verifier = verifier;
+ return TRUE;
+}
+
+static xmlnode *scram_start(JabberStream *js, xmlnode *mechanisms)
+{
+ xmlnode *reply;
+ JabberScramData *data;
+ guint64 cnonce;
+#ifdef CHANNEL_BINDING
+ gboolean binding_supported = TRUE;
+#endif
+ gchar *dec_out, *enc_out;
+
+ data = js->auth_mech_data = g_new0(JabberScramData, 1);
+ data->hash = mech_to_hash(js->auth_mech->name);
+
+#ifdef CHANNEL_BINDING
+ if (strstr(js->auth_mech_name, "-PLUS"))
+ data->channel_binding = TRUE;
+#endif
+ cnonce = ((guint64)g_random_int() << 32) | g_random_int();
+ data->cnonce = purple_base64_encode((guchar *)cnonce, sizeof(cnonce));
+
+ data->auth_message = g_string_new(NULL);
+ g_string_printf(data->auth_message, "n=%s,r=%s",
+ js->user->node /* TODO: SaslPrep */,
+ data->cnonce);
+
+ reply = xmlnode_new("auth");
+ xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
+ xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name);
+
+ /* TODO: Channel binding */
+ dec_out = g_strdup_printf("%c,,%s", 'n', data->auth_message->str);
+ enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
+ purple_debug_misc("jabber", "initial SCRAM message '%s'\n", dec_out);
+
+ xmlnode_insert_data(reply, enc_out, -1);
+
+ g_free(enc_out);
+ g_free(dec_out);
+
+ return reply;
+}
+
+static xmlnode *scram_handle_challenge(JabberStream *js, xmlnode *challenge)
+{
+ JabberScramData *data = js->auth_mech_data;
+ xmlnode *reply;
+
+ gchar *enc_in, *dec_in;
+ gchar *enc_out, *dec_out;
+ gsize decoded_size;
+
+ gchar *enc_proof;
+
+ gchar *nonce;
+ GString *salt;
+ guint iterations;
+
+ g_return_val_if_fail(data != NULL, NULL);
+
+ enc_in = xmlnode_get_data(challenge);
+ /* TODO: Error handling */
+ g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', NULL);
+
+ dec_in = (gchar *)purple_base64_decode(enc_in, &decoded_size);
+ g_free(enc_in);
+ if (!dec_in || decoded_size != strlen(dec_in)) {
+ /* Danger afoot; SCRAM shouldn't contain NUL bytes */
+ /* TODO: Error handling */
+ g_free(dec_in);
+ return NULL;
+ }
+
+ purple_debug_misc("jabber", "decoded challenge (%" G_GSIZE_FORMAT "): %s\n",
+ decoded_size, dec_in);
+
+ g_string_append_c(data->auth_message, ',');
+ g_string_append(data->auth_message, dec_in);
+
+ if (!parse_challenge(data, dec_in, &nonce, &salt, &iterations)) {
+ /* TODO: Error handling */
+ return NULL;
+ }
+
+ g_string_append_c(data->auth_message, ',');
+ /* "biwsCg==" is the base64 encoding of "n,,". I promise. */
+ g_string_append_printf(data->auth_message, "c=%s,r=%s", "biwsCg==", nonce);
+#ifdef CHANNEL_BINDING
+ #error fix this
+#endif
+
+ if (!jabber_scram_calc_proofs(data, purple_connection_get_password(js->gc), salt, iterations)) {
+ /* TODO: Error handling */
+ return NULL;
+ }
+
+ reply = xmlnode_new("response");
+ xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
+
+ enc_proof = purple_base64_encode((guchar *)data->client_proof->str, data->client_proof->len);
+ dec_out = g_strdup_printf("c=%s,r=%s,p=%s", "biwsCg==", nonce, enc_proof);
+ enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
+
+ purple_debug_misc("jabber", "decoded response (%" G_GSIZE_FORMAT "): %s\n",
+ strlen(dec_out), dec_out);
+
+ xmlnode_insert_data(reply, enc_out, -1);
+
+ g_free(enc_out);
+ g_free(dec_out);
+ g_free(enc_proof);
+
+ return reply;
+}
+
+static gboolean scram_handle_success(JabberStream *js, xmlnode *packet)
+{
+ JabberScramData *data = js->auth_mech_data;
+ char *enc_in, *dec_in;
+ char *enc_server_signature;
+ guchar *server_signature;
+ gsize decoded_size;
+
+ enc_in = xmlnode_get_data(packet);
+ /* TODO: Error handling */
+ g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', FALSE);
+
+ dec_in = (gchar *)purple_base64_decode(enc_in, &decoded_size);
+ g_free(enc_in);
+ if (!dec_in || decoded_size != strlen(dec_in)) {
+ /* Danger afoot; SCRAM shouldn't contain NUL bytes */
+ /* TODO: Error handling */
+ g_free(dec_in);
+ return FALSE;
+ }
+
+ purple_debug_misc("jabber", "decoded success (%" G_GSIZE_FORMAT "): %s\n",
+ decoded_size, dec_in);
+
+ if (!parse_success(data, dec_in, &enc_server_signature)) {
+ /* TODO: Error handling */
+ return FALSE;
+ }
+
+ server_signature = purple_base64_decode(enc_server_signature, &decoded_size);
+ if (server_signature == NULL) {
+ /* TODO: Error handling */
+ return FALSE;
+ }
+
+ if (decoded_size != data->server_signature->len) {
+ /* TODO: Error handling */
+ purple_debug_error("jabber", "SCRAM server signature wrong length (was "
+ "(was %" G_GSIZE_FORMAT ", expected %" G_GSIZE_FORMAT ")\n",
+ decoded_size, data->server_signature->len);
+ return FALSE;
+ }
+
+ if (0 != memcmp(server_signature, data->server_signature->str, decoded_size)) {
+ /* TODO: Error handling */
+ purple_debug_error("jabber", "SCRAM server signature did not match!\n");
+ return FALSE;
+ }
+
+ /* Hooray */
+ return TRUE;
+}
+
+static void scram_dispose(JabberStream *js)
+{
+ if (js->auth_mech_data) {
+ JabberScramData *data = js->auth_mech_data;
+
+ g_free(data->cnonce);
+ if (data->auth_message)
+ g_string_free(data->auth_message, TRUE);
+ if (data->client_proof)
+ g_string_free(data->client_proof, TRUE);
+ if (data->server_signature)
+ g_string_free(data->server_signature, TRUE);
+ g_free(data);
+ js->auth_mech_data = NULL;
+ }
+}
+
+static JabberSaslMech scram_sha1_mech = {
+ 50, /* priority */
+ "SCRAM-SHA-1", /* name */
+ scram_start,
+ scram_handle_challenge,
+ scram_handle_success,
+ NULL, /* handle_failure */
+ scram_dispose
+};
+
+#ifdef CHANNEL_BINDING
+/* With channel binding */
+static JabberSaslMech scram_sha1_plus_mech = {
+ scram_sha1_mech.priority + 1, /* priority */
+ "SCRAM-SHA-1-PLUS", /* name */
+ scram_start,
+ scram_handle_challenge,
+ scram_handle_success,
+ NULL, /* handle_failure */
+ scram_dispose
+};
+#endif
+
+/* For tests */
+JabberSaslMech *jabber_scram_get_sha1(void)
+{
+ return &scram_sha1_mech;
+}
+
+JabberSaslMech **jabber_auth_get_scram_mechs(gint *count)
+{
+ static JabberSaslMech *mechs[] = {
+ &scram_sha1_mech,
+#ifdef CHANNEL_BINDING
+ &scram_sha1_plus_mech,
+#endif
+ };
+
+ g_return_val_if_fail(count != NULL, NULL);
+
+ *count = G_N_ELEMENTS(mechs);
+ return mechs;
+}
============================================================
--- libpurple/protocols/jabber/auth_scram.h 5df4da2c5fa2734caceca71a34b3f7f4357304d5
+++ libpurple/protocols/jabber/auth_scram.h c2d2a43170a4e75c9a4420f4eb4075ef262326fc
@@ -24,6 +24,29 @@
#ifndef PURPLE_JABBER_AUTH_SCRAM_H_
#define PURPLE_JABBER_AUTH_SCRAM_H_
+/*
+ * Every function in this file is ONLY exposed for tests.
+ * DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR.
+ */
+
+/* Per-connection state stored between messages.
+ * This is stored in js->auth_data_mech.
+ */
+
+typedef struct {
+ const char *hash;
+ char *cnonce;
+ GString *auth_message;
+
+ GString *client_proof;
+ GString *server_signature;
+ gboolean channel_binding;
+} JabberScramData;
+
+#include "auth.h"
+
+JabberSaslMech *jabber_scram_get_sha1(void);
+
/**
* Implements the Hi() function as described in the SASL-SCRAM I-D.
*
@@ -34,9 +57,26 @@
* @param salt The salt.
* @param iterations The number of iterations to perform.
*
- * @returns A newly allocated string containing the result.
+ * @returns A newly allocated string containing the result. The string is
+ * NOT null-terminated and its length is the length of the binary
+ * output of the hash function in-use.
*/
-GString *jabber_auth_scram_hi(const char *hash, const GString *str,
- GString *salt, guint iterations);
+guchar *jabber_scram_hi(const char *hash, const GString *str,
+ GString *salt, guint iterations);
+/**
+ * Calculates the proofs as described in Section 3 of the SASL-SCRAM I-D.
+ *
+ * @param data A JabberScramData structure. hash and auth_message must be
+ * set. client_proof and server_signature will be set as a result
+ * of this function.
+ * @param password The user's password.
+ * @param salt The salt (as specified by the server)
+ * @param iterations The number of iterations to perform.
+ *
+ * @returns TRUE if the proofs were successfully calculated. FALSE otherwise.
+ */
+gboolean jabber_scram_calc_proofs(JabberScramData *data, const char *password,
+ GString *salt, guint iterations);
+
#endif /* PURPLE_JABBER_AUTH_SCRAM_H_ */
============================================================
--- libpurple/protocols/jabber/jabber.c 3380fe836fd52ecaf4df0f18df3a9ff870a5b712
+++ libpurple/protocols/jabber/jabber.c 1286dc53f7b28ef35d62b271e66128ba73059f06
@@ -1501,6 +1501,8 @@ void jabber_close(PurpleConnection *gc)
purple_circ_buffer_destroy(js->write_buffer);
if(js->writeh)
purple_input_remove(js->writeh);
+ if (js->auth_mech && js->auth_mech->dispose)
+ js->auth_mech->dispose(js);
#ifdef HAVE_CYRUS_SASL
if(js->sasl)
sasl_dispose(&js->sasl);
============================================================
--- libpurple/protocols/jabber/jabber.h 6b8715d89aabdb10a06e50612fe8ebc6ae91ff98
+++ libpurple/protocols/jabber/jabber.h 569d76b910429dfd9e0e3efb28d8765c00c330d4
@@ -107,6 +107,7 @@ struct _JabberStream
} protocol_version;
JabberSaslMech *auth_mech;
+ gpointer auth_mech_data;
char *stream_id;
JabberStreamState state;
============================================================
--- libpurple/tests/test_jabber_scram.c 9a0ca3018e20eaa895a8908ad5bf17f55f930d30
+++ libpurple/tests/test_jabber_scram.c e1ac9975df228f6315ab555cabc8f6e462b3919c
@@ -4,14 +4,14 @@
#include "../util.h"
#include "../protocols/jabber/auth_scram.h"
+static JabberSaslMech *scram_sha1_mech = NULL;
+
#define assert_pbkdf2_equal(password, salt, count, expected) { \
GString *p = g_string_new(password); \
GString *s = g_string_new(salt); \
- GString *result = jabber_auth_scram_hi("sha1", p, s, count); \
+ guchar *result = jabber_scram_hi("sha1", p, s, count); \
fail_if(result == NULL, "Hi() returned NULL"); \
- fail_if(result->len != 20, "Hi() returned with unexpected length %u", result->len); \
- fail_if(0 != memcmp(result->str, expected, 20), "Hi() returned invalid result"); \
- g_string_free(result, TRUE); \
+ fail_if(0 != memcmp(result, expected, 20), "Hi() returned invalid result"); \
g_string_free(s, TRUE); \
g_string_free(p, TRUE); \
}
@@ -19,9 +19,7 @@ START_TEST(test_pbkdf2)
START_TEST(test_pbkdf2)
{
assert_pbkdf2_equal("password", "salt", 1, "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6");
-
assert_pbkdf2_equal("password", "salt", 2, "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57");
-
assert_pbkdf2_equal("password", "salt", 4096, "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1");
#if 0
@@ -31,6 +29,40 @@ END_TEST
}
END_TEST
+START_TEST(test_proofs)
+{
+ JabberScramData *data = g_new0(JabberScramData, 1);
+ gboolean ret;
+ GString *salt;
+ const char *client_proof;
+/* const char *server_signature; */
+
+ data->hash = "sha1";
+ data->auth_message = g_string_new("n=username at jabber.org,r=8jLxB5515dhFxBil5A0xSXMH,"
+ "r=8jLxB5515dhFxBil5A0xSXMHabc,s=c2FsdA==,i=1,"
+ "c=biws,r=8jLxB5515dhFxBil5A0xSXMHabc");
+ client_proof = "\x48\x61\x30\xa5\x61\x0b\xae\xb9\xe4\x11\xa8\xfd\xa5\xcd\x34\x1d\x8a\x3c\x28\x17";
+
+ salt = g_string_new("salt");
+ ret = jabber_scram_calc_proofs(data, "password", salt, 1);
+ fail_if(ret == FALSE, "Failed to calculate SCRAM proofs!");
+
+ fail_unless(0 == memcmp(client_proof, data->client_proof->str, 20));
+ g_string_free(salt, TRUE);
+ g_string_free(data->auth_message, TRUE);
+ g_free(data);
+}
+END_TEST
+
+#if 0
+START_TEST(test_mech)
+{
+ scram_sha1_mech = jabber_scram_get_sha1();
+
+}
+END_TEST
+#endif
+
Suite *
jabber_scram_suite(void)
{
@@ -40,5 +72,9 @@ jabber_scram_suite(void)
tcase_add_test(tc, test_pbkdf2);
suite_add_tcase(s, tc);
+ tc = tcase_create("SCRAM Proofs");
+ tcase_add_test(tc, test_proofs);
+ suite_add_tcase(s, tc);
+
return s;
}
More information about the Commits
mailing list