guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str, GString *salt, guint iterations) { PurpleHash *hasher; PurpleCipher *cipher; guchar *result; guint i; guchar *prev, *tmp; g_return_val_if_fail(hash != NULL, NULL); g_return_val_if_fail(str != NULL && str->len > 0, NULL); g_return_val_if_fail(salt != NULL && salt->len > 0, NULL); g_return_val_if_fail(iterations > 0, NULL); prev = g_new0(guint8, hash->size); tmp = g_new0(guint8, hash->size); result = g_new0(guint8, hash->size); hasher = hash->new_cipher(); cipher = purple_hmac_cipher_new(hasher); g_object_unref(G_OBJECT(hasher)); /* Append INT(1), a four-octet encoding of the integer 1, most significant * octet first. */ g_string_append_len(salt, "\0\0\0\1", 4); /* Compute U0 */ purple_cipher_set_key(cipher, (guchar *)str->str, str->len); purple_cipher_append(cipher, (guchar *)salt->str, salt->len); purple_cipher_digest(cipher, result, hash->size); memcpy(prev, result, hash->size); /* Compute U1...Ui */ for (i = 1; i < iterations; ++i) { guint j; purple_cipher_reset(cipher); purple_cipher_set_key(cipher, (guchar *)str->str, str->len); purple_cipher_append(cipher, prev, hash->size); purple_cipher_digest(cipher, tmp, hash->size); for (j = 0; j < hash->size; ++j) result[j] ^= tmp[j]; memcpy(prev, tmp, hash->size); } g_object_unref(G_OBJECT(cipher)); g_free(tmp); g_free(prev); return result; }
static void purple_aes_cipher_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { PurpleCipher *cipher = PURPLE_CIPHER(obj); switch(param_id) { case PROP_BATCH_MODE: purple_cipher_set_batch_mode(cipher, g_value_get_enum(value)); break; case PROP_IV: { guchar *iv = (guchar *)g_value_get_string(value); purple_cipher_set_iv(cipher, iv, strlen((gchar*)iv)); } break; case PROP_KEY: purple_cipher_set_key(cipher, (guchar *)g_value_get_string(value), purple_aes_cipher_get_key_size(cipher)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } }
/* * Helper functions for doing the SCRAM calculations. The first 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 JabberScramHash *hash, guchar *out, const guchar *key, const gchar *str) { PurpleHash *hasher; PurpleCipher *cipher; hasher = hash->new_cipher(); cipher = purple_hmac_cipher_new(hasher); g_object_unref(G_OBJECT(hasher)); purple_cipher_set_key(cipher, key, hash->size); purple_cipher_append(cipher, (guchar *)str, strlen(str)); purple_cipher_digest(cipher, out, hash->size); g_object_unref(G_OBJECT(cipher)); }
static void auth_old_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *packet, gpointer data) { JabberIq *iq; PurpleXmlNode *query, *x; const char *pw = purple_connection_get_password(js->gc); if (type == JABBER_IQ_ERROR) { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = jabber_parse_error(js, packet, &reason); purple_connection_error(js->gc, reason, msg); g_free(msg); } else if (type == JABBER_IQ_RESULT) { query = purple_xmlnode_get_child(packet, "query"); if (js->stream_id && *js->stream_id && purple_xmlnode_get_child(query, "digest")) { char *s, *hash; iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "digest"); s = g_strdup_printf("%s%s", js->stream_id, pw); hash = jabber_calculate_data_hash(s, strlen(s), "sha1"); purple_xmlnode_insert_data(x, hash, -1); g_free(hash); g_free(s); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if ((x = purple_xmlnode_get_child(query, "crammd5"))) { /* For future reference, this appears to be a custom OS X extension * to non-SASL authentication. */ const char *challenge; gchar digest[33]; PurpleCipher *hmac; PurpleHash *md5; gssize diglen; /* Calculate the MHAC-MD5 digest */ md5 = purple_md5_hash_new(); hmac = purple_hmac_cipher_new(md5); challenge = purple_xmlnode_get_attrib(x, "challenge"); purple_cipher_set_key(hmac, (guchar *)pw, strlen(pw)); purple_cipher_append(hmac, (guchar *)challenge, strlen(challenge)); diglen = purple_cipher_digest_to_str(hmac, digest, 33); g_object_unref(hmac); g_object_unref(md5); g_return_if_fail(diglen > 0); /* Create the response query */ iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "crammd5"); purple_xmlnode_insert_data(x, digest, 32); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if(purple_xmlnode_get_child(query, "password")) { PurpleAccount *account = purple_connection_get_account(js->gc); if(!jabber_stream_is_ssl(js) && !purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) { char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), purple_account_get_username(account)); purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, 1, purple_request_cpar_from_account(account), account, allow_plaintext_auth, disallow_plaintext_auth); g_free(msg); return; } finish_plaintext_authentication(js); } else { purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, _("Server does not use any supported authentication method")); return; } } }
static void cipher_test_aes(void) { PurpleCipher *cipher; int i = 0; gboolean fail = FALSE; purple_debug_info("cipher-test", "Running AES tests\n"); cipher = purple_aes_cipher_new(); if (cipher == NULL) { purple_debug_error("cipher-test", "AES cipher not found\n"); fail = TRUE; } while (!fail && aes_tests[i].cipher) { aes_test *test = &aes_tests[i]; gsize key_size; guchar *key; guchar cipher_s[1024], decipher_s[1024]; ssize_t cipher_len, decipher_len; gchar *cipher_b16, *deciphered; purple_debug_info("cipher-test", "Test %02d:\n", i); purple_debug_info("cipher-test", "\tTesting '%s' (%" G_GSIZE_FORMAT "bit) \n", test->plaintext ? test->plaintext : "(null)", strlen(test->key) * 8 / 2); i++; purple_cipher_reset(cipher); if (test->iv) { gsize iv_size; guchar *iv = purple_base16_decode(test->iv, &iv_size); g_assert(iv != NULL); purple_cipher_set_iv(cipher, iv, iv_size); g_free(iv); } key = purple_base16_decode(test->key, &key_size); g_assert(key != NULL); purple_cipher_set_key(cipher, key, key_size); g_free(key); if (purple_cipher_get_key_size(cipher) != key_size) { purple_debug_info("cipher-test", "\tinvalid key size\n"); fail = TRUE; continue; } cipher_len = purple_cipher_encrypt(cipher, (const guchar*)(test->plaintext ? test->plaintext : ""), test->plaintext ? (strlen(test->plaintext) + 1) : 0, cipher_s, sizeof(cipher_s)); if (cipher_len < 0) { purple_debug_info("cipher-test", "\tencryption failed\n"); fail = TRUE; continue; } cipher_b16 = purple_base16_encode(cipher_s, cipher_len); purple_debug_info("cipher-test", "\tGot: %s\n", cipher_b16); purple_debug_info("cipher-test", "\tWanted: %s\n", test->cipher); if (g_strcmp0(cipher_b16, test->cipher) != 0) { purple_debug_info("cipher-test", "\tencrypted data doesn't match\n"); g_free(cipher_b16); fail = TRUE; continue; } g_free(cipher_b16); decipher_len = purple_cipher_decrypt(cipher, cipher_s, cipher_len, decipher_s, sizeof(decipher_s)); if (decipher_len < 0) { purple_debug_info("cipher-test", "\tdecryption failed\n"); fail = TRUE; continue; } deciphered = (decipher_len > 0) ? (gchar*)decipher_s : NULL; if (g_strcmp0(deciphered, test->plaintext) != 0) { purple_debug_info("cipher-test", "\tdecrypted data doesn't match\n"); fail = TRUE; continue; } purple_debug_info("cipher-test", "\tTest OK\n"); } if (cipher != NULL) g_object_unref(cipher); if (fail) purple_debug_info("cipher-test", "AES tests FAILED\n\n"); else purple_debug_info("cipher-test", "AES tests completed successfully\n\n"); }
static void cipher_test_pbkdf2(void) { PurpleCipher *cipher; PurpleHash *hash; int i = 0; gboolean fail = FALSE; purple_debug_info("cipher-test", "Running PBKDF2 tests\n"); while (!fail && pbkdf2_tests[i].answer) { pbkdf2_test *test = &pbkdf2_tests[i]; gchar digest[2 * 32 + 1 + 10]; gchar *digest_nss = NULL; gboolean ret, skip_nss = FALSE; i++; purple_debug_info("cipher-test", "Test %02d:\n", i); purple_debug_info("cipher-test", "\tTesting '%s' with salt:'%s' hash:%s iter_count:%d \n", test->passphrase, test->salt, test->hash, test->iter_count); if (!strcmp(test->hash, "sha1")) hash = purple_sha1_hash_new(); else if (!strcmp(test->hash, "sha256")) hash = purple_sha256_hash_new(); else hash = NULL; cipher = purple_pbkdf2_cipher_new(hash); g_object_set(G_OBJECT(cipher), "iter_count", GUINT_TO_POINTER(test->iter_count), NULL); g_object_set(G_OBJECT(cipher), "out_len", GUINT_TO_POINTER(test->out_len), NULL); purple_cipher_set_salt(cipher, (const guchar*)test->salt, test->salt ? strlen(test->salt): 0); purple_cipher_set_key(cipher, (const guchar*)test->passphrase, strlen(test->passphrase)); ret = purple_cipher_digest_to_str(cipher, digest, sizeof(digest)); purple_cipher_reset(cipher); if (!ret) { purple_debug_info("cipher-test", "\tfailed\n"); fail = TRUE; g_object_unref(cipher); g_object_unref(hash); continue; } if (g_strcmp0(test->hash, "sha1") != 0) skip_nss = TRUE; if (test->out_len != 16 && test->out_len != 32) skip_nss = TRUE; #ifdef HAVE_NSS if (!skip_nss) { digest_nss = cipher_pbkdf2_nss_sha1(test->passphrase, test->salt, test->iter_count, test->out_len); } #else skip_nss = TRUE; #endif purple_debug_info("cipher-test", "\tGot: %s\n", digest); if (digest_nss) purple_debug_info("cipher-test", "\tGot from NSS: %s\n", digest_nss); purple_debug_info("cipher-test", "\tWanted: %s\n", test->answer); if (g_strcmp0(digest, test->answer) == 0 && (skip_nss || g_strcmp0(digest, digest_nss) == 0)) { purple_debug_info("cipher-test", "\tTest OK\n"); } else { purple_debug_info("cipher-test", "\twrong answer\n"); fail = TRUE; } g_object_unref(cipher); g_object_unref(hash); } if (fail) purple_debug_info("cipher-test", "PBKDF2 tests FAILED\n\n"); else purple_debug_info("cipher-test", "PBKDF2 tests completed successfully\n\n"); }