changeset 10684:72a5babfa8b4

[gaim-migrate @ 12231] the cipher api that grim has been working on for ages is finally done!! big congrats and thanks to him!! lots of modified files in this commit. it builds here. moved the md5 files to src/protocols/oscar so that it continues to depend on nothing in gaim. everything else uses the new centralized cipher api. I'm not sure if src/md5.* needs to be removed or not, so I left it there. someone let me know or do it directly. someone check if these need to be added to potfiles.in and let there be much rejoicing! committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Fri, 11 Mar 2005 13:05:31 +0000
parents e11f3e1599d4
children 27cd496c4665
files plugins/ciphertest.c plugins/signals-test.c src/Makefile.am src/cipher.c src/cipher.h src/core.c src/protocols/jabber/auth.c src/protocols/jabber/buddy.c src/protocols/jabber/presence.c src/protocols/jabber/si.c src/protocols/msn/msn.h src/protocols/msn/notification.c src/protocols/msn/user.c src/protocols/oscar/Makefile.am src/protocols/oscar/md5.c src/protocols/oscar/md5.h src/protocols/yahoo/crypt.c src/protocols/yahoo/yahoo.c src/proxy.c src/value.h
diffstat 20 files changed, 2450 insertions(+), 229 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/ciphertest.c	Fri Mar 11 13:05:31 2005 +0000
@@ -0,0 +1,209 @@
+/*
+ * A plugin to test the ciphers that ship with gaim
+ *
+ * Copyright (C) 2004, Gary Kramlich <amc_grim@users.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef GAIM_PLUGINS
+#define GAIM_PLUGINS
+#endif
+
+#include "internal.h"
+
+#include <glib.h>
+#include <string.h>
+
+#include "cipher.h"
+#include "debug.h"
+#include "plugin.h"
+#include "version.h"
+
+struct test {
+	const guint8 *question;
+	const guint8 *answer;
+};
+
+/**************************************************************************
+ * MD5 Stuff
+ **************************************************************************/
+struct test md5_tests[8] = {
+	{							"",	"d41d8cd98f00b204e9800998ecf8427e"},
+	{						   "a",	"0cc175b9c0f1b6a831c399e269772661"},
+	{						 "abc",	"900150983cd24fb0d6963f7d28e17f72"},
+	{			  "message digest",	"f96b697d7cb7938d525a2f31aaf161d0"},
+	{ "abcdefghijklmnopqrstuvwxyz",	"c3fcd3d76192e4007dfb496cca67e13b"},
+	{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	  "abcdefghijklmnopqrstuvwxyz"
+					  "0123456789",	"d174ab98d277d9f5a5611c2c9f419d9f"},
+	{"123456789012345678901234567"
+	 "890123456789012345678901234"
+	  "56789012345678901234567890",	"57edf4a22be3c955ac49da2e2107b67a"},
+	{						  NULL, NULL							  }
+};
+
+static void
+cipher_test_md5() {
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	gchar digest[32];
+	gint i = 0;
+
+	cipher = gaim_ciphers_find_cipher("md5");
+	if(!cipher) {
+		gaim_debug_info("cipher-test",
+						"could not find md5 cipher, not testing\n");
+		return;
+	}
+
+	gaim_debug_info("cipher-test", "Running md5 tests\n");
+
+	context = gaim_cipher_context_new(cipher, NULL);
+
+	while(md5_tests[i].answer) {
+		gaim_debug_info("cipher-test", "Test %02d:\n", i);
+		gaim_debug_info("cipher-test", "Testing '%s'\n", md5_tests[i].question);
+
+		gaim_cipher_context_append(context, md5_tests[i].question,
+								   strlen(md5_tests[i].question));
+
+		gaim_cipher_context_digest_to_str(context, NULL, digest);
+
+		gaim_debug_info("cipher-test", "\tGot:    %s\n", digest);
+		gaim_debug_info("cipher-test", "\tWanted: %s\n", md5_tests[i].answer);
+
+		gaim_cipher_context_reset(context, NULL);
+		i++;
+	}
+
+	gaim_cipher_context_destroy(context);
+
+	gaim_debug_info("cipher-test", "md5 tests completed\n\n");
+}
+
+/**************************************************************************
+ * SHA-1 stuff
+ **************************************************************************/
+struct test sha1_tests[5] = {
+	{"a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"},
+	{"abc", "a9993e364706816aba3e25717850c26c9cd0d89d"} ,
+	{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"} ,
+    {NULL, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"},
+	{NULL, NULL}
+};
+
+static void
+cipher_test_sha1() {
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	gchar digest[40];
+	gint i = 0;
+
+	cipher = gaim_ciphers_find_cipher("sha1");
+	if(!cipher) {
+		gaim_debug_info("cipher-test",
+						"could not find sha1 cipher, not testing\n");
+		return;
+	}
+
+	gaim_debug_info("cipher-test", "Running sha1 tests\n");
+
+	context = gaim_cipher_context_new(cipher, NULL);
+
+	while(sha1_tests[i].answer) {
+		gaim_debug_info("cipher-test", "Test %02d:\n", i);
+		gaim_debug_info("cipher-test", "Testing '%s'\n",
+						(sha1_tests[i].question != NULL) ?
+						sha1_tests[i].question : "'a'x1000, 1000 times");
+
+		if(sha1_tests[i].question) {
+			gaim_cipher_context_append(context, sha1_tests[i].question,
+									   strlen(sha1_tests[i].question));
+		} else {
+			gint j;
+			guint8 buff[1000];
+
+			memset(buff, 'a', 1000);
+
+			for(j = 0; j < 1000; j++)
+				gaim_cipher_context_append(context, buff, 1000);
+		}
+
+		gaim_cipher_context_digest_to_str(context, NULL, digest);
+
+		gaim_debug_info("cipher-test", "\tGot:    %s\n", digest);
+		gaim_debug_info("cipher-test", "\tWanted: %s\n", sha1_tests[i].answer);
+
+		gaim_cipher_context_reset(context, NULL);
+		i++;
+	}
+
+	gaim_cipher_context_destroy(context);
+
+	gaim_debug_info("cipher-test", "sha1 tests completed\n\n");
+}
+/**************************************************************************
+ * Plugin stuff
+ **************************************************************************/
+static gboolean
+plugin_load(GaimPlugin *plugin) {
+	cipher_test_md5();
+	cipher_test_sha1();
+
+	return TRUE;
+}
+
+static GaimPluginInfo info =
+{
+	GAIM_PLUGIN_MAGIC,
+	GAIM_MAJOR_VERSION,
+	GAIM_MINOR_VERSION,
+	GAIM_PLUGIN_STANDARD,								/**< type           */
+	NULL,												/**< ui_requirement */
+	0,													/**< flags          */
+	NULL,												/**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,								/**< priority       */
+
+	"core-cipher-test",									/**< id             */
+	N_("Cipher Test"),									/**< name           */
+	VERSION,											/**< version        */
+														/**  summary        */
+	N_("Tests the ciphers that ship with gaim."),
+														/**  description    */
+	N_("Tests the ciphers that ship with gaim."),
+	"Gary Kramlich <amc_grim@users.sf.net>",			/**< author         */
+	GAIM_WEBSITE,										/**< homepage       */
+
+	plugin_load,										/**< load           */
+	NULL,												/**< unload         */
+	NULL,												/**< destroy        */
+
+	NULL,												/**< ui_info        */
+	NULL,												/**< extra_info     */
+	NULL,
+	NULL
+};
+
+static void
+init_plugin(GaimPlugin *plugin) {
+}
+
+GAIM_INIT_PLUGIN(ciper_test, init_plugin, info)
--- a/plugins/signals-test.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/plugins/signals-test.c	Fri Mar 11 13:05:31 2005 +0000
@@ -23,6 +23,7 @@
 #include <stdio.h>
 
 #include "internal.h"
+#include "cipher.h"
 #include "connection.h"
 #include "conversation.h"
 #include "core.h"
@@ -439,6 +440,21 @@
 					(who) ? who : "unknown");
 }
 /**************************************************************************
+ * Ciphers signal callbacks
+ **************************************************************************/
+static void
+cipher_added_cb(GaimCipher *cipher, void *data) {
+	gaim_debug_misc("signals test", "cipher %s added\n",
+					gaim_cipher_get_name(cipher));
+}
+
+static void
+cipher_removed_cb(GaimCipher *cipher, void *data) {
+	gaim_debug_misc("signals test", "cipher %s removed\n",
+					gaim_cipher_get_name(cipher));
+}
+
+/**************************************************************************
  * Core signal callbacks
  **************************************************************************/
 static void
@@ -458,6 +474,7 @@
 	void *conn_handle = gaim_connections_get_handle();
 	void *conv_handle = gaim_conversations_get_handle();
 	void *accounts_handle = gaim_accounts_get_handle();
+	void *ciphers_handle = gaim_ciphers_get_handle();
 
 	/* Accounts subsystem signals */
 	gaim_signal_connect(accounts_handle, "account-connecting",
@@ -563,6 +580,12 @@
 	gaim_signal_connect(conv_handle, "chat-topic-changed",
 						plugin, GAIM_CALLBACK(chat_topic_changed_cb), NULL);
 
+	/* Ciphers signals */
+	gaim_signal_connect(ciphers_handle, "cipher-added",
+						plugin, GAIM_CALLBACK(cipher_added_cb), NULL);
+	gaim_signal_connect(ciphers_handle, "cipher-removed",
+						plugin, GAIM_CALLBACK(cipher_removed_cb), NULL);
+
 	/* Core signals */
 	gaim_signal_connect(core_handle, "quitting",
 						plugin, GAIM_CALLBACK(quitting_cb), NULL);
--- a/src/Makefile.am	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/Makefile.am	Fri Mar 11 13:05:31 2005 +0000
@@ -65,6 +65,7 @@
 	accountopt.c \
 	blist.c \
 	buddyicon.c \
+	cipher.c \
 	cmds.c \
 	connection.c \
 	conversation.c \
@@ -75,7 +76,6 @@
 	ft.c \
 	imgstore.c \
 	log.c \
-	md5.c \
 	network.c \
 	notify.c \
 	plugin.c \
@@ -90,7 +90,6 @@
 	roomlist.c \
 	savedstatuses.c \
 	server.c \
-	sha.c \
 	signals.c \
 	status.c \
 	stringref.c \
@@ -105,6 +104,7 @@
 	accountopt.h \
 	blist.h \
 	buddyicon.h \
+	cipher.h \
 	cmds.h \
 	connection.h \
 	conversation.h \
@@ -115,7 +115,6 @@
 	ft.h \
 	imgstore.h \
 	log.h \
-	md5.h \
 	network.h \
 	notify.h \
 	plugin.h \
@@ -130,7 +129,6 @@
 	roomlist.h \
 	savedstatuses.h \
 	server.h \
-	sha.h \
 	signals.h \
 	status.h \
 	stringref.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cipher.c	Fri Mar 11 13:05:31 2005 +0000
@@ -0,0 +1,1071 @@
+/*
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * Original md5
+ * Copyright (C) 2001-2003  Christophe Devine <c.devine@cr0.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "internal.h"
+#include "cipher.h"
+#include "debug.h"
+#include "signals.h"
+#include "value.h"
+
+/*******************************************************************************
+ * MD5
+ ******************************************************************************/
+struct MD5Context {
+	guint32 total[2];
+	guint32 state[4];
+	guint8 buffer[64];
+};
+
+#define MD5_GET_GUINT32(n,b,i) {			\
+	(n) = ((guint32)(b) [(i)    ]      )	\
+		| ((guint32)(b) [(i) + 1] <<  8)	\
+		| ((guint32)(b) [(i) + 2] << 16)	\
+		| ((guint32)(b) [(i) + 3] << 24);	\
+}
+#define MD5_PUT_GUINT32(n,b,i) { 			\
+	(b)[(i)    ] = (guint8)((n)      );		\
+    (b)[(i) + 1] = (guint8)((n) >>  8);     \
+	(b)[(i) + 2] = (guint8)((n) >> 16); 	\
+	(b)[(i) + 3] = (guint8)((n) >> 24); 	\
+}
+
+static void
+md5_init(GaimCipherContext *context, gpointer extra) {
+	struct MD5Context *md5_context;
+
+	md5_context = g_new0(struct MD5Context, 1);
+
+	gaim_cipher_context_set_data(context, md5_context);
+
+	gaim_cipher_context_reset(context, extra);
+}
+
+static void
+md5_reset(GaimCipherContext *context, gpointer extra) {
+	struct MD5Context *md5_context;
+
+	md5_context = gaim_cipher_context_get_data(context);
+
+	md5_context->total[0] = 0;
+	md5_context->total[1] = 0;
+
+	md5_context->state[0] = 0x67452301;
+	md5_context->state[1] = 0xEFCDAB89;
+	md5_context->state[2] = 0x98BADCFE;
+	md5_context->state[3] = 0x10325476;
+
+	memset(md5_context->buffer, 0, sizeof(md5_context->buffer));
+}
+
+static void
+md5_uninit(GaimCipherContext *context) {
+	struct MD5Context *md5_context;
+
+	gaim_cipher_context_reset(context, NULL);
+
+	md5_context = gaim_cipher_context_get_data(context);
+	memset(md5_context, 0, sizeof(md5_context));
+
+	g_free(md5_context);
+	md5_context = NULL;
+}
+
+static void
+md5_process(struct MD5Context *md5_context, const guint8 data[64]) {
+	guint32 X[16], A, B, C, D;
+
+	A = md5_context->state[0];
+	B = md5_context->state[1];
+	C = md5_context->state[2];
+	D = md5_context->state[3];
+
+	MD5_GET_GUINT32(X[ 0], data,  0);
+	MD5_GET_GUINT32(X[ 1], data,  4);
+	MD5_GET_GUINT32(X[ 2], data,  8);
+	MD5_GET_GUINT32(X[ 3], data, 12);
+	MD5_GET_GUINT32(X[ 4], data, 16);
+	MD5_GET_GUINT32(X[ 5], data, 20);
+	MD5_GET_GUINT32(X[ 6], data, 24);
+	MD5_GET_GUINT32(X[ 7], data, 28);
+	MD5_GET_GUINT32(X[ 8], data, 32);
+	MD5_GET_GUINT32(X[ 9], data, 36);
+	MD5_GET_GUINT32(X[10], data, 40);
+	MD5_GET_GUINT32(X[11], data, 44);
+	MD5_GET_GUINT32(X[12], data, 48);
+	MD5_GET_GUINT32(X[13], data, 52);
+	MD5_GET_GUINT32(X[14], data, 56);
+	MD5_GET_GUINT32(X[15], data, 60);
+
+	#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+	#define P(a,b,c,d,k,s,t) {		\
+		a += F(b,c,d) + X[k] + t;	\
+		a = S(a,s) + b;				\
+	}
+
+	/* first pass */
+	#define F(x,y,z) (z ^ (x & (y ^ z)))
+	P(A, B, C, D,  0,  7, 0xD76AA478);
+	P(D, A, B, C,  1, 12, 0xE8C7B756);
+	P(C, D, A, B,  2, 17, 0x242070DB);
+	P(B, C, D, A,  3, 22, 0xC1BDCEEE);
+	P(A, B, C, D,  4,  7, 0xF57C0FAF);
+	P(D, A, B, C,  5, 12, 0x4787C62A);
+	P(C, D, A, B,  6, 17, 0xA8304613);
+	P(B, C, D, A,  7, 22, 0xFD469501);
+	P(A, B, C, D,  8,  7, 0x698098D8);
+	P(D, A, B, C,  9, 12, 0x8B44F7AF);
+	P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+	P(B, C, D, A, 11, 22, 0x895CD7BE);
+	P(A, B, C, D, 12,  7, 0x6B901122);
+	P(D, A, B, C, 13, 12, 0xFD987193);
+	P(C, D, A, B, 14, 17, 0xA679438E);
+	P(B, C, D, A, 15, 22, 0x49B40821);
+	#undef F
+
+	/* second pass */
+	#define F(x,y,z) (y ^ (z & (x ^ y)))
+	P(A, B, C, D,  1,  5, 0xF61E2562);
+	P(D, A, B, C,  6,  9, 0xC040B340);
+	P(C, D, A, B, 11, 14, 0x265E5A51);
+	P(B, C, D, A,  0, 20, 0xE9B6C7AA);
+	P(A, B, C, D,  5,  5, 0xD62F105D);
+	P(D, A, B, C, 10,  9, 0x02441453);
+	P(C, D, A, B, 15, 14, 0xD8A1E681);
+	P(B, C, D, A,  4, 20, 0xE7D3FBC8);
+	P(A, B, C, D,  9,  5, 0x21E1CDE6);
+	P(D, A, B, C, 14,  9, 0xC33707D6);
+	P(C, D, A, B,  3, 14, 0xF4D50D87);
+	P(B, C, D, A,  8, 20, 0x455A14ED);
+	P(A, B, C, D, 13,  5, 0xA9E3E905);
+	P(D, A, B, C,  2,  9, 0xFCEFA3F8);
+	P(C, D, A, B,  7, 14, 0x676F02D9);
+	P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+	#undef F
+    
+	/* third pass */
+	#define F(x,y,z) (x ^ y ^ z)
+	P(A, B, C, D,  5,  4, 0xFFFA3942);
+	P(D, A, B, C,  8, 11, 0x8771F681);
+	P(C, D, A, B, 11, 16, 0x6D9D6122);
+	P(B, C, D, A, 14, 23, 0xFDE5380C);
+	P(A, B, C, D,  1,  4, 0xA4BEEA44);
+	P(D, A, B, C,  4, 11, 0x4BDECFA9);
+	P(C, D, A, B,  7, 16, 0xF6BB4B60);
+	P(B, C, D, A, 10, 23, 0xBEBFBC70);
+	P(A, B, C, D, 13,  4, 0x289B7EC6);
+	P(D, A, B, C,  0, 11, 0xEAA127FA);
+	P(C, D, A, B,  3, 16, 0xD4EF3085);
+	P(B, C, D, A,  6, 23, 0x04881D05);
+	P(A, B, C, D,  9,  4, 0xD9D4D039);
+	P(D, A, B, C, 12, 11, 0xE6DB99E5);
+	P(C, D, A, B, 15, 16, 0x1FA27CF8);
+	P(B, C, D, A,  2, 23, 0xC4AC5665);
+	#undef F
+
+	/* forth pass */
+	#define F(x,y,z) (y ^ (x | ~z))
+	P(A, B, C, D,  0,  6, 0xF4292244);
+	P(D, A, B, C,  7, 10, 0x432AFF97);
+	P(C, D, A, B, 14, 15, 0xAB9423A7);
+	P(B, C, D, A,  5, 21, 0xFC93A039);
+	P(A, B, C, D, 12,  6, 0x655B59C3);
+	P(D, A, B, C,  3, 10, 0x8F0CCC92);
+	P(C, D, A, B, 10, 15, 0xFFEFF47D);
+	P(B, C, D, A,  1, 21, 0x85845DD1);
+	P(A, B, C, D,  8,  6, 0x6FA87E4F);
+	P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+	P(C, D, A, B,  6, 15, 0xA3014314);
+	P(B, C, D, A, 13, 21, 0x4E0811A1);
+	P(A, B, C, D,  4,  6, 0xF7537E82);
+	P(D, A, B, C, 11, 10, 0xBD3AF235);
+	P(C, D, A, B,  2, 15, 0x2AD7D2BB);
+	P(B, C, D, A,  9, 21, 0xEB86D391);
+	#undef F
+	#undef P
+	#undef S
+
+	md5_context->state[0] += A;
+	md5_context->state[1] += B;
+	md5_context->state[2] += C;
+	md5_context->state[3] += D;
+}
+
+static void
+md5_append(GaimCipherContext *context, const guint8 *data, size_t len) {
+	struct MD5Context *md5_context = NULL;
+	guint32 left = 0, fill = 0;
+
+	g_return_if_fail(context != NULL);
+
+	md5_context = gaim_cipher_context_get_data(context);
+	g_return_if_fail(md5_context != NULL);
+
+	left = md5_context->total[0] & 0x3F;
+	fill = 64 - left;
+
+	md5_context->total[0] += len;
+	md5_context->total[0] &= 0xFFFFFFFF;
+
+	if(md5_context->total[0] < len)
+		md5_context->total[1]++;
+
+	if(left && len >= fill) {
+		memcpy((md5_context->buffer + left), data, fill);
+		md5_process(md5_context, md5_context->buffer);
+		len -= fill;
+		data += fill;
+		left = 0;
+	}
+
+	while(len >= 64) {
+		md5_process(md5_context, data);
+		len -= 64;
+		data += 64;
+	}
+
+	if(len) {
+		memcpy((md5_context->buffer + left), data, len);
+	}
+}
+
+static gboolean
+md5_digest(GaimCipherContext *context, size_t *len, guint8 digest[16]) {
+	struct MD5Context *md5_context = NULL;
+	guint32 last, pad;
+	guint32 high, low;
+	guint8 message[8];
+	guint8 padding[64] = {
+		0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+	};
+
+	g_return_val_if_fail(len, FALSE);
+	g_return_val_if_fail(*len >= 16, FALSE);
+
+	md5_context = gaim_cipher_context_get_data(context);
+
+	high = (md5_context->total[0] >> 29)
+		 | (md5_context->total[1] << 3);
+	low = (md5_context->total[0] << 3);
+
+	MD5_PUT_GUINT32(low, message, 0);
+	MD5_PUT_GUINT32(high, message, 4);
+
+	last = md5_context->total[0] & 0x3F;
+	pad = (last < 56) ? (56 - last) : (120 - last);
+
+	md5_append(context, padding, pad);
+	md5_append(context, message, 8);
+
+	MD5_PUT_GUINT32(md5_context->state[0], digest, 0);
+	MD5_PUT_GUINT32(md5_context->state[1], digest, 4);
+	MD5_PUT_GUINT32(md5_context->state[2], digest, 8);
+	MD5_PUT_GUINT32(md5_context->state[3], digest, 12);
+
+	return TRUE;
+}
+
+static GaimCipherOps MD5Ops = {
+	NULL,			/* Set option */
+	NULL,			/* Get option */
+	md5_init,		/* init */
+	md5_reset,		/* reset */
+	md5_uninit,		/* uninit */
+	NULL,			/* set iv */
+	md5_append,		/* append */
+	md5_digest,		/* digest */
+	NULL,			/* encrypt */
+	NULL,			/* decrypt */
+	NULL,			/* set salt */
+	NULL,			/* get salt size */
+	NULL,			/* set key */
+	NULL			/* get key size */
+};
+
+/*******************************************************************************
+ * SHA-1
+ ******************************************************************************/
+#define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF)
+
+struct SHA1Context {
+	guint32 H[5];
+	guint32 W[80];
+
+    gint lenW;
+
+	guint32 sizeHi;
+	guint32 sizeLo;
+};
+
+static void
+sha1_hash_block(struct SHA1Context *sha1_ctx) {
+	gint i;
+	guint32 A, B, C, D, E, T;
+
+	for(i = 16; i < 80; i++) {
+		sha1_ctx->W[i] = SHA1_ROTL(sha1_ctx->W[i -  3] ^
+								   sha1_ctx->W[i -  8] ^
+								   sha1_ctx->W[i - 14] ^
+								   sha1_ctx->W[i - 16], 1);
+	}
+
+	A = sha1_ctx->H[0];
+	B = sha1_ctx->H[1];
+	C = sha1_ctx->H[2];
+	D = sha1_ctx->H[3];
+	E = sha1_ctx->H[4];
+
+	for(i = 0; i < 20; i++) {
+		T = (SHA1_ROTL(A, 5) + (((C ^ D) & B) ^ D) + E + sha1_ctx->W[i] + 0x5A827999) & 0xFFFFFFFF;
+		E = D;
+		D = C;
+		C = SHA1_ROTL(B, 30);
+		B = A;
+		A = T;
+	}
+
+	for(i = 20; i < 40; i++) {
+		T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0x6ED9EBA1) & 0xFFFFFFFF;
+		E = D;
+		D = C;
+		C = SHA1_ROTL(B, 30);
+		B = A;
+		A = T;
+	}
+
+	for(i = 40; i < 60; i++) {
+		T = (SHA1_ROTL(A, 5) + ((B & C) | (D & (B | C))) + E + sha1_ctx->W[i] + 0x8F1BBCDC) & 0xFFFFFFFF;
+		E = D;
+		D = C;
+		C = SHA1_ROTL(B, 30);
+		B = A;
+		A = T;
+	}
+
+	for(i = 60; i < 80; i++) {
+		T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0xCA62C1D6) & 0xFFFFFFFF;
+		E = D;
+		D = C;
+		C = SHA1_ROTL(B, 30);
+		B = A;
+		A = T;
+	}
+
+	sha1_ctx->H[0] += A;
+	sha1_ctx->H[1] += B;
+	sha1_ctx->H[2] += C;
+	sha1_ctx->H[3] += D;
+	sha1_ctx->H[4] += E;	
+}
+
+static void
+sha1_set_opt(GaimCipherContext *context, const gchar *name, void *value) {
+	struct SHA1Context *ctx;
+
+	ctx = gaim_cipher_context_get_data(context);
+
+	if(!strcmp(name, "sizeHi")) {
+		ctx->sizeHi = GPOINTER_TO_INT(value);
+	} else if(!strcmp(name, "sizeLo")) {
+		ctx->sizeLo = GPOINTER_TO_INT(value);
+	} else if(!strcmp(name, "lenW")) {
+		ctx->lenW = GPOINTER_TO_INT(value);
+	}
+}
+
+static void *
+sha1_get_opt(GaimCipherContext *context, const gchar *name) {
+	struct SHA1Context *ctx;
+
+	ctx = gaim_cipher_context_get_data(context);
+
+	if(!strcmp(name, "sizeHi")) {
+		return GINT_TO_POINTER(ctx->sizeHi);
+	} else if(!strcmp(name, "sizeLo")) {
+		return GINT_TO_POINTER(ctx->sizeLo);
+	} else if(!strcmp(name, "lenW")) {
+		return GINT_TO_POINTER(ctx->lenW);
+	}
+
+	return NULL;
+}
+
+static void
+sha1_init(GaimCipherContext *context, void *extra) {
+	struct SHA1Context *sha1_ctx;
+
+	sha1_ctx = g_new0(struct SHA1Context, 1);
+
+	gaim_cipher_context_set_data(context, sha1_ctx);
+
+	gaim_cipher_context_reset(context, extra);
+}
+
+static void
+sha1_reset(GaimCipherContext *context, void *extra) {
+	struct SHA1Context *sha1_ctx;
+	gint i;
+
+	sha1_ctx = gaim_cipher_context_get_data(context);
+
+	g_return_if_fail(sha1_ctx);
+
+	sha1_ctx->lenW = 0;
+	sha1_ctx->sizeHi = 0;
+	sha1_ctx->sizeLo = 0;
+
+	sha1_ctx->H[0] = 0x67452301;
+	sha1_ctx->H[1] = 0xEFCDAB89;
+	sha1_ctx->H[2] = 0x98BADCFE;
+	sha1_ctx->H[3] = 0x10325476;
+	sha1_ctx->H[4] = 0xC3D2E1F0;
+
+	for(i = 0; i < 80; i++)
+		sha1_ctx->W[i] = 0;
+}
+
+static void
+sha1_uninit(GaimCipherContext *context) {
+	struct SHA1Context *sha1_ctx;
+
+	gaim_cipher_context_reset(context, NULL);
+
+	sha1_ctx = gaim_cipher_context_get_data(context);
+
+	memset(sha1_ctx, 0, sizeof(struct SHA1Context));
+
+	g_free(sha1_ctx);
+	sha1_ctx = NULL;
+}
+
+
+static void
+sha1_append(GaimCipherContext *context, const guint8 *data, size_t len) {
+	struct SHA1Context *sha1_ctx;
+	gint i;
+
+	sha1_ctx = gaim_cipher_context_get_data(context);
+
+	g_return_if_fail(sha1_ctx);
+
+	for(i = 0; i < len; i++) {
+		sha1_ctx->W[sha1_ctx->lenW / 4] <<= 8;
+		sha1_ctx->W[sha1_ctx->lenW / 4] |= data[i];
+
+		if((++sha1_ctx->lenW) % 64 == 0) {
+			sha1_hash_block(sha1_ctx);
+			sha1_ctx->lenW = 0;
+		}
+
+		sha1_ctx->sizeLo += 8;
+		sha1_ctx->sizeHi += (sha1_ctx->sizeLo < 8);
+	}
+}
+
+static gboolean
+sha1_digest(GaimCipherContext *context, size_t *len, guint8 digest[20]) {
+	struct SHA1Context *sha1_ctx;
+	guint8 pad0x80 = 0x80, pad0x00 = 0x00;
+	guint8 padlen[8];
+	gint i;
+
+	g_return_val_if_fail(len, FALSE);
+	g_return_val_if_fail(*len <= 20, FALSE);
+
+	sha1_ctx = gaim_cipher_context_get_data(context);
+
+	g_return_val_if_fail(sha1_ctx, FALSE);
+
+	padlen[0] = (guint8)((sha1_ctx->sizeHi >> 24) & 255);
+	padlen[1] = (guint8)((sha1_ctx->sizeHi >> 16) & 255);
+	padlen[2] = (guint8)((sha1_ctx->sizeHi >> 8) & 255);
+	padlen[3] = (guint8)((sha1_ctx->sizeHi >> 0) & 255);
+	padlen[4] = (guint8)((sha1_ctx->sizeLo >> 24) & 255);
+	padlen[5] = (guint8)((sha1_ctx->sizeLo >> 16) & 255);
+	padlen[6] = (guint8)((sha1_ctx->sizeLo >> 8) & 255);
+	padlen[7] = (guint8)((sha1_ctx->sizeLo >> 0) & 255);
+
+	/* pad with a 1, then zeroes, then length */
+	gaim_cipher_context_append(context, &pad0x80, 1);
+	while(sha1_ctx->lenW != 56)
+		gaim_cipher_context_append(context, &pad0x00, 1);
+	gaim_cipher_context_append(context, padlen, 8);
+
+	for(i = 0; i < 20; i++) {
+		digest[i] = (guint8)(sha1_ctx->H[i / 4] >> 24);
+		sha1_ctx->H[i / 4] <<= 8;
+	}
+
+	gaim_cipher_context_reset(context, NULL);
+
+	return TRUE;
+}
+
+static GaimCipherOps SHA1Ops = {
+	sha1_set_opt,	/* Set Option		*/
+	sha1_get_opt,	/* Get Option		*/
+	sha1_init,		/* init				*/
+	sha1_reset,		/* reset			*/
+	sha1_uninit,	/* uninit			*/
+	NULL,			/* set iv			*/
+	sha1_append,	/* append			*/
+	sha1_digest,	/* digest			*/
+	NULL,			/* encrypt			*/
+	NULL,			/* decrypt			*/
+	NULL,			/* set salt			*/
+	NULL,			/* get salt size	*/
+	NULL,			/* set key			*/
+	NULL			/* get key size		*/
+};
+
+/*******************************************************************************
+ * Structs
+ ******************************************************************************/
+struct _GaimCipher {
+	gchar *name;
+	GaimCipherOps *ops;
+	guint ref;
+};
+
+struct _GaimCipherContext {
+	GaimCipher *cipher;
+	gpointer data;
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GList *ciphers = NULL;
+
+/******************************************************************************
+ * GaimCipher API
+ *****************************************************************************/
+const gchar *
+gaim_cipher_get_name(GaimCipher *cipher) {
+	g_return_val_if_fail(cipher, NULL);
+
+	return cipher->name;
+}
+
+guint
+gaim_cipher_get_capabilities(GaimCipher *cipher) {
+	GaimCipherOps *ops = NULL;
+	guint caps = 0;
+
+	g_return_val_if_fail(cipher, 0);
+
+	ops = cipher->ops;
+	g_return_val_if_fail(ops, 0);
+
+	if(ops->set_option)
+		caps |= GAIM_CIPHER_CAPS_SET_OPT;
+	if(ops->get_option)
+		caps |= GAIM_CIPHER_CAPS_GET_OPT;
+	if(ops->init)
+		caps |= GAIM_CIPHER_CAPS_INIT;
+	if(ops->reset)
+		caps |= GAIM_CIPHER_CAPS_RESET;
+	if(ops->uninit)
+		caps |= GAIM_CIPHER_CAPS_UNINIT;
+	if(ops->set_iv)
+		caps |= GAIM_CIPHER_CAPS_SET_IV;
+	if(ops->append)
+		caps |= GAIM_CIPHER_CAPS_APPEND;
+	if(ops->digest)
+		caps |= GAIM_CIPHER_CAPS_DIGEST;
+	if(ops->encrypt)
+		caps |= GAIM_CIPHER_CAPS_ENCRYPT;
+	if(ops->decrypt)
+		caps |= GAIM_CIPHER_CAPS_DECRYPT;
+	if(ops->set_salt)
+		caps |= GAIM_CIPHER_CAPS_SET_SALT;
+	if(ops->get_salt_size)
+		caps |= GAIM_CIPHER_CAPS_GET_SALT_SIZE;
+	if(ops->set_key)
+		caps |= GAIM_CIPHER_CAPS_SET_KEY;
+	if(ops->get_key_size)
+		caps |= GAIM_CIPHER_CAPS_GET_KEY_SIZE;
+
+	return caps;
+}
+
+void
+gaim_cipher_digest_region(const gchar *name, const guint8 *data,
+						  size_t data_len, guint8 digest[], size_t *digest_len)
+{
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+
+	g_return_if_fail(name);
+	g_return_if_fail(data);
+
+	cipher = gaim_ciphers_find_cipher(name);
+
+	g_return_if_fail(cipher);
+
+	if(!cipher->ops->append || !cipher->ops->digest) {
+		gaim_debug_info("cipher", "gaim_cipher_region failed: "
+						"the %s cipher does not support appending and or "
+						"digesting.", cipher->name);
+		return;	
+	}
+
+	context = gaim_cipher_context_new(cipher, NULL);
+	gaim_cipher_context_append(context, data, data_len);
+	gaim_cipher_context_digest(context, digest_len, digest);
+	gaim_cipher_context_destroy(context);
+}
+
+/******************************************************************************
+ * GaimCiphers API
+ *****************************************************************************/
+GaimCipher *
+gaim_ciphers_find_cipher(const gchar *name) {
+	GaimCipher *cipher;
+	GList *l;
+
+	g_return_val_if_fail(name, NULL);
+
+	for(l = ciphers; l; l = l->next) {
+		cipher = GAIM_CIPHER(l->data);
+
+		if(!g_ascii_strcasecmp(cipher->name, name))
+			return cipher;
+	}
+
+	return NULL;
+}
+
+GaimCipher *
+gaim_ciphers_register_cipher(const gchar *name, GaimCipherOps *ops) {
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(name, NULL);
+	g_return_val_if_fail(ops, NULL);
+	g_return_val_if_fail(!gaim_ciphers_find_cipher(name), NULL);
+
+	cipher = g_new0(GaimCipher, 1);
+
+	cipher->name = g_strdup(name);
+	cipher->ops = ops;
+
+	ciphers = g_list_append(ciphers, cipher);
+
+	gaim_signal_emit(gaim_ciphers_get_handle(), "cipher-added", cipher);
+
+	return cipher;
+}
+
+gboolean
+gaim_ciphers_unregister_cipher(GaimCipher *cipher) {
+	g_return_val_if_fail(cipher, FALSE);
+	g_return_val_if_fail(cipher->ref == 0, FALSE);
+
+	gaim_signal_emit(gaim_ciphers_get_handle(), "cipher-removed", cipher);
+
+	ciphers = g_list_remove(ciphers, cipher);
+
+	g_free(cipher->name);
+	g_free(cipher);
+
+	return TRUE;
+}
+
+GList *
+gaim_ciphers_get_ciphers() {
+	return ciphers;
+}
+
+/******************************************************************************
+ * GaimCipher Subsystem API
+ *****************************************************************************/
+gpointer
+gaim_ciphers_get_handle() {
+	static gint handle;
+
+	return &handle;
+}
+
+void
+gaim_ciphers_init() {
+	gpointer handle;
+
+	handle = gaim_ciphers_get_handle();
+
+	gaim_signal_register(handle, "cipher-added",
+						 gaim_marshal_VOID__POINTER, NULL, 1,
+						 gaim_value_new(GAIM_TYPE_SUBTYPE,
+										GAIM_SUBTYPE_CIPHER));
+	gaim_signal_register(handle, "cipher-removed",
+						 gaim_marshal_VOID__POINTER, NULL, 1,
+						 gaim_value_new(GAIM_TYPE_SUBTYPE,
+										GAIM_SUBTYPE_CIPHER));
+
+	gaim_ciphers_register_cipher("md5", &MD5Ops);
+	gaim_ciphers_register_cipher("sha1", &SHA1Ops);
+}
+
+void
+gaim_ciphers_uninit() {
+	GaimCipher *cipher;
+	GList *l, *ll;
+
+	for(l = ciphers; l; l = ll) {
+		ll = l->next;
+
+		cipher = GAIM_CIPHER(l->data);
+		gaim_ciphers_unregister_cipher(cipher);
+
+		ciphers = g_list_remove(ciphers, cipher);
+	}
+
+	g_list_free(ciphers);
+
+	gaim_signals_unregister_by_instance(gaim_ciphers_get_handle());
+}
+/******************************************************************************
+ * GaimCipherContext API
+ *****************************************************************************/
+void
+gaim_cipher_context_set_option(GaimCipherContext *context, const gchar *name,
+							   gpointer value)
+{
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+	g_return_if_fail(name);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_option)
+		cipher->ops->set_option(context, name, value);
+	else
+		gaim_debug_info("cipher", "the %s cipher does not support the "
+						"set_option operation\n", cipher->name);
+}
+
+gpointer
+gaim_cipher_context_get_option(GaimCipherContext *context, const gchar *name) {
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, NULL);
+	g_return_val_if_fail(name, NULL);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, NULL);
+
+	if(cipher->ops && cipher->ops->get_option)
+		return cipher->ops->get_option(context, name);
+	else {
+		gaim_debug_info("cipher", "the %s cipher does not support the "
+						"get_option operation\n", cipher->name);
+
+		return NULL;
+	}
+}
+
+GaimCipherContext *
+gaim_cipher_context_new(GaimCipher *cipher, void *extra) {
+	GaimCipherContext *context = NULL;
+
+	g_return_val_if_fail(cipher, NULL);
+
+	cipher->ref++;
+
+	context = g_new0(GaimCipherContext, 1);
+	context->cipher = cipher;
+
+	if(cipher->ops->init)
+		cipher->ops->init(context, extra);
+
+	return context;
+}
+
+GaimCipherContext *
+gaim_cipher_context_new_by_name(const gchar *name, void *extra) {
+	GaimCipher *cipher;
+
+	g_return_val_if_fail(name, NULL);
+
+	cipher = gaim_ciphers_find_cipher(name);
+
+	g_return_val_if_fail(cipher, NULL);
+
+	return gaim_cipher_context_new(cipher, extra);
+}
+
+void
+gaim_cipher_context_reset(GaimCipherContext *context, void *extra) {
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->reset)
+		context->cipher->ops->reset(context, extra);
+}
+
+void
+gaim_cipher_context_destroy(GaimCipherContext *context) {
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	cipher->ref--;
+
+	if(cipher->ops && cipher->ops->uninit)
+		cipher->ops->uninit(context);
+
+	memset(context, 0, sizeof(context));
+	g_free(context);
+	context = NULL;
+}
+
+void
+gaim_cipher_context_set_iv(GaimCipherContext *context, guint8 *iv, size_t len)
+{
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+	g_return_if_fail(iv);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_iv)
+		cipher->ops->set_iv(context, iv, len);
+	else
+		gaim_debug_info("cipher", "the %s cipher does not support the set"
+						"initialization vector operation\n", cipher->name);
+}
+
+void
+gaim_cipher_context_append(GaimCipherContext *context, const guint8 *data,
+								size_t len)
+{
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->append)
+		cipher->ops->append(context, data, len);
+	else
+		gaim_debug_info("cipher", "the %s cipher does not support the append "
+						"operation\n", cipher->name);
+}
+
+gboolean
+gaim_cipher_context_digest(GaimCipherContext *context, size_t *len,
+						   guint8 digest[])
+{
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, FALSE);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(context, FALSE);
+
+	if(cipher->ops && cipher->ops->digest)
+		return cipher->ops->digest(context, len, digest);
+	else {
+		gaim_debug_info("cipher", "the %s cipher does not support the digest "
+						"operation\n", cipher->name);
+		return FALSE;
+	}
+}
+
+gboolean
+gaim_cipher_context_digest_to_str(GaimCipherContext *context, size_t *len,
+								   gchar digest_s[])
+{
+	/* 16k is a bit excessive, will tweak later. */
+	guint8 digest[BUF_LEN * 4];
+	gint n = 0;
+	size_t dlen = 0;
+
+	g_return_val_if_fail(context, FALSE);
+	g_return_val_if_fail(digest_s, FALSE);
+
+	if(!gaim_cipher_context_digest(context, &dlen, digest))
+		return FALSE;
+
+	dlen *= 2;
+
+	if(len)
+		*len = dlen;
+
+	for(n = 0; n < dlen; n++)
+		sprintf(digest_s + (n * 2), "%02x", digest[n]);
+
+	digest_s[n * 2] = '\0';
+
+	return TRUE;
+}
+
+gint
+gaim_cipher_context_encrypt(GaimCipherContext *context, const guint8 data[],
+							size_t len, guint8 output[], size_t *outlen)
+{
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->encrypt)
+		return cipher->ops->encrypt(context, data, len, output, outlen);
+	else {
+		gaim_debug_info("cipher", "the %s cipher does not support the encrypt"
+						"operation\n", cipher->name);
+
+		if(outlen)
+			*outlen = -1;
+
+		return -1;
+	}
+}
+
+gint
+gaim_cipher_context_decrypt(GaimCipherContext *context, const guint8 data[],
+							size_t len, guint8 output[], size_t *outlen)
+{
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->decrypt)
+		return cipher->ops->decrypt(context, data, len, output, outlen);
+	else {
+		gaim_debug_info("cipher", "the %s cipher does not support the decrypt"
+						"operation\n", cipher->name);
+
+		if(outlen)
+			*outlen = -1;
+
+		return -1;
+	}
+}
+
+void
+gaim_cipher_context_set_salt(GaimCipherContext *context, guint8 *salt) {
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_salt)
+		cipher->ops->set_salt(context, salt);
+	else
+		gaim_debug_info("cipher", "the %s cipher does not support the "
+						"set_salt operation\n", cipher->name);
+}
+
+size_t
+gaim_cipher_context_get_salt_size(GaimCipherContext *context) {
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_salt_size)
+		return cipher->ops->get_salt_size(context);
+	else {
+		gaim_debug_info("cipher", "the %s cipher does not support the "
+						"get_salt_size operation\n", cipher->name);
+
+		return -1;
+	}
+}
+
+void
+gaim_cipher_context_set_key(GaimCipherContext *context, guint8 *key) {
+	GaimCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_key)
+		cipher->ops->set_key(context, key);
+	else
+		gaim_debug_info("cipher", "the %s cipher does not support the "
+						"set_key operation\n", cipher->name);
+}
+
+size_t
+gaim_cipher_context_get_key_size(GaimCipherContext *context) {
+	GaimCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_key_size)
+		return cipher->ops->get_key_size(context);
+	else {
+		gaim_debug_info("cipher", "the %s cipher does not support the "
+						"get_key_size operation\n", cipher->name);
+
+		return -1;
+	}
+}
+
+void
+gaim_cipher_context_set_data(GaimCipherContext *context, gpointer data) {
+	g_return_if_fail(context);
+
+	context->data = data;
+}
+
+gpointer
+gaim_cipher_context_get_data(GaimCipherContext *context) {
+	g_return_val_if_fail(context, NULL);
+
+	return context->data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cipher.h	Fri Mar 11 13:05:31 2005 +0000
@@ -0,0 +1,390 @@
+/**
+ * @file cipher.h Gaim Cipher API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef GAIM_CIPHER_H
+#define GAIM_CIPHER_H
+
+#include <glib.h>
+
+#define GAIM_CIPHER(obj)			((GaimCipher *)(obj))			/**< GaimCipher typecast helper			*/
+#define GAIM_CIPHER_OPS(obj)		((GaimCipherOps *)(obj))		/**< GaimCipherInfo typecase helper		*/
+#define GAIM_CIPHER_CONTEXT(obj)	((GaimCipherContext *)(obj))	/**< GaimCipherContext typecast helper	*/
+
+typedef struct _GaimCipher			GaimCipher;			/**< A handle to a GaimCipher	*/
+typedef struct _GaimCipherOps		GaimCipherOps;		/**< Ops for a GaimCipher		*/
+typedef struct _GaimCipherContext	GaimCipherContext;	/**< A context for a GaimCipher	*/
+
+
+/**
+ * The operation flags for a cipher
+ */
+typedef enum _GaimCipherCaps {
+	GAIM_CIPHER_CAPS_SET_OPT			= 1 << 1,		/**< Set option flag	*/
+	GAIM_CIPHER_CAPS_GET_OPT			= 1 << 2,		/**< Get option flag	*/
+	GAIM_CIPHER_CAPS_INIT				= 1 << 3,		/**< Init flag			*/
+	GAIM_CIPHER_CAPS_RESET				= 1 << 4,		/**< Reset flag			*/
+	GAIM_CIPHER_CAPS_UNINIT				= 1 << 5,		/**< Uninit flag		*/
+	GAIM_CIPHER_CAPS_SET_IV				= 1 << 6,		/**< Set IV flag		*/
+	GAIM_CIPHER_CAPS_APPEND				= 1 << 7,		/**< Append flag		*/
+	GAIM_CIPHER_CAPS_DIGEST				= 1 << 8,		/**< Digest flag		*/
+	GAIM_CIPHER_CAPS_ENCRYPT			= 1 << 9,		/**< Encrypt flag		*/
+	GAIM_CIPHER_CAPS_DECRYPT			= 1 << 10,		/**< Decrypt flag		*/
+	GAIM_CIPHER_CAPS_SET_SALT			= 1 << 11,		/**< Set salt flag		*/
+	GAIM_CIPHER_CAPS_GET_SALT_SIZE		= 1 << 12,		/**< Get salt size flag	*/
+	GAIM_CIPHER_CAPS_SET_KEY			= 1 << 13,		/**< Set key flag		*/
+	GAIM_CIPHER_CAPS_GET_KEY_SIZE		= 1 << 14,		/**< Get key size flag	*/
+	GAIM_CIPHER_CAPS_UNKNOWN			= 1 << 16		/**< Unknown			*/
+} GaimCipherCaps;
+
+/**
+ * The operations of a cipher.  Every cipher must implement one of these.
+ */
+struct _GaimCipherOps {
+	/** The set option function	*/
+	void (*set_option)(GaimCipherContext *context, const gchar *name, void *value);
+
+	/** The get option function */
+	void *(*get_option)(GaimCipherContext *context, const gchar *name);
+
+	/** The init function */
+	void (*init)(GaimCipherContext *context, void *extra);
+
+	/** The reset function */
+	void (*reset)(GaimCipherContext *context, void *extra);
+
+	/** The uninit function */
+	void (*uninit)(GaimCipherContext *context);
+
+	/** The set initialization vector function */
+	void (*set_iv)(GaimCipherContext *context, guint8 *iv, size_t len);
+
+	/** The append data function */
+	void (*append)(GaimCipherContext *context, const guint8 *data, size_t len);
+
+	/** The digest function */
+	gboolean (*digest)(GaimCipherContext *context, size_t *len, guint8 digest[]);
+
+	/** The encrypt function */
+	int (*encrypt)(GaimCipherContext *context, const guint8 data[], size_t len, guint8 output[], size_t *outlen);
+
+	/** The decrypt function */
+	int (*decrypt)(GaimCipherContext *context, const guint8 data[], size_t len, guint8 output[], size_t *outlen); 
+
+	/** The set salt function */
+	void (*set_salt)(GaimCipherContext *context, guint8 *salt);
+
+	/** The get salt size function */
+	size_t (*get_salt_size)(GaimCipherContext *context);
+
+	/** The set key function */
+	void (*set_key)(GaimCipherContext *context, guint8 *key);
+
+	/** The get key size function */
+	size_t (*get_key_size)(GaimCipherContext *context);
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*****************************************************************************/
+/** @name GaimCipher API													 */
+/*****************************************************************************/
+/*@{*/
+
+/**
+ * Gets a cipher's name
+ *
+ * @param cipher The cipher handle
+ *
+ * @return The cipher's name
+ */
+const gchar *gaim_cipher_get_name(GaimCipher *cipher);
+
+/**
+ * Gets a cipher's capabilities
+ *
+ * @param cipher The cipher handle
+ *
+ * @return The cipher's info
+ */
+guint gaim_cipher_get_capabilities(GaimCipher *cipher);
+
+/**
+ * Gets a digest from a cipher
+ *
+ * @param name       The cipher's name
+ * @param data       The data to hash
+ * @param data_len   The length of the data
+ * @param digest     The returned digest
+ * @param digest_len The returned digest's length
+ */
+void gaim_cipher_digest_region(const gchar *name, const guint8 *data, size_t data_len, guint8 digest[], size_t *digest_len); 
+
+/*@}*/
+/******************************************************************************/
+/** @name GaimCiphers API													  */
+/******************************************************************************/
+/*@{*/
+
+/**
+ * Finds a cipher by it's name
+ *
+ * @param name The name of the cipher to find
+ *
+ * @return The cipher handle or @c NULL
+ */
+GaimCipher *gaim_ciphers_find_cipher(const gchar *name);
+
+/**
+ * Registers a cipher as a usable cipher
+ *
+ * @param name The name of the new cipher
+ * @param ops  The cipher ops to register
+ *
+ * @return The handle to the new cipher or @c NULL if it failed
+ */
+GaimCipher *gaim_ciphers_register_cipher(const gchar *name, GaimCipherOps *ops);
+
+/**
+ * Unregisters a cipher
+ *
+ * @param cipher The cipher handle to unregister
+ *
+ * @return Whether or not the cipher was successfully unloaded
+ */
+gboolean gaim_ciphers_unregister_cipher(GaimCipher *cipher);
+
+/**
+ * Gets the list of ciphers
+ *
+ * @return The list of available ciphers
+ * @note This list should not be modified, it is owned by the cipher core
+ */
+GList *gaim_ciphers_get_ciphers();
+
+/*@}*/
+/******************************************************************************/
+/** @name GaimCipher Subsystem API											  */
+/******************************************************************************/
+/*@{*/
+
+/**
+ * Gets the handle to the cipher subsystem
+ *
+ * @return The handle to the cipher subsystem
+ */
+gpointer gaim_ciphers_get_handle();
+
+/**
+ * Initializes the cipher core
+ */
+void gaim_ciphers_init();
+
+/**
+ * Uninitializes the cipher core
+ */
+void gaim_ciphers_uninit();
+
+/*@}*/
+/******************************************************************************/
+/** @name GaimCipherContext API												  */
+/******************************************************************************/
+/*@{*/
+
+/**
+ * Sets the value an option on a cipher context
+ *
+ * @param context The cipher context
+ * @param name    The name of the option
+ * @param value   The value to set
+ */
+void gaim_cipher_context_set_option(GaimCipherContext *context, const gchar *name, gpointer value);
+
+/**
+ * Gets the vale of an option on a cipher context
+ *
+ * @param context The cipher context
+ * @param name    The name of the option
+ * @return The value of the option
+ */
+gpointer gaim_cipher_context_get_option(GaimCipherContext *context, const gchar *name);
+
+/**
+ * Creates a new cipher context and initializes it
+ *
+ * @param cipher The cipher to use
+ * @param extra  Extra data for the specific cipher
+ *
+ * @return The new cipher context
+ */
+GaimCipherContext *gaim_cipher_context_new(GaimCipher *cipher, void *extra);
+
+/**
+ * Creates a new cipher context by the cipher name and initializes it
+ *
+ * @param name  The cipher's name
+ * @param extra Extra data for the specific cipher
+ *
+ * @return The new cipher context
+ */
+GaimCipherContext *gaim_cipher_context_new_by_name(const gchar *name, void *extra);
+
+/**
+ * Resets a cipher context to it's default value
+ * @note If you have set an IV you will have to set it after resetting
+ *
+ * @param context The context to reset
+ * @param extra   Extra data for the specific cipher
+ */
+void gaim_cipher_context_reset(GaimCipherContext *context, gpointer extra);
+
+/**
+ * Destorys a cipher context and deinitializes it
+ *
+ * @param context The cipher context to destory
+ */
+void gaim_cipher_context_destroy(GaimCipherContext *context);
+
+/**
+ * Sets the initialization vector for a context
+ * @note This should only be called right after a cipher context is created or reset
+ *
+ * @param context The context to set the IV to
+ * @param iv      The initialization vector to set
+ * @param len     The len of the IV
+ */
+void gaim_cipher_context_set_iv(GaimCipherContext *context, guint8 *iv, size_t len);
+
+/**
+ * Appends data to the context
+ *
+ * @param context The context to append data to
+ * @param data    The data to append
+ * @param len     The length of the data
+ */
+void gaim_cipher_context_append(GaimCipherContext *context, const guint8 *data, size_t len);
+
+/**
+ * Digests a context
+ *
+ * @param context The context to digest
+ * @param len     The length of the returned value
+ * @param digest  The return buffer for the digest
+ */
+gboolean gaim_cipher_context_digest(GaimCipherContext *context, size_t *len, guint8 digest[]);
+
+/**
+ * Converts a guint8 digest into a hex string
+ *
+ * @param context  The context to get a digest from
+ * @param len      The length of the returned value
+ * @param digest_s The return buffer for the string digest
+ */
+gboolean gaim_cipher_context_digest_to_str(GaimCipherContext *context, size_t *len, gchar digest_s[]);
+
+/**
+ * Encrypts data using the context
+ *
+ * @param context The context
+ * @param data    The data to encrypt
+ * @param len     The length of the data
+ * @param output  The output buffer
+ * @param outlen  The len of data that was outputed
+ *
+ * @return A cipher specific status code
+ */
+gint gaim_cipher_context_encrypt(GaimCipherContext *context, const guint8 data[], size_t len, guint8 output[], size_t *outlen);
+
+/**
+ * Decrypts data using the context
+ *
+ * @param context The context
+ * @param data    The data to encrypt
+ * @param len     The length of the returned value
+ * @param output  The output buffer
+ * @param outlen  The len of data that was outputed
+ *
+ * @return A cipher specific status code
+ */
+gint gaim_cipher_context_decrypt(GaimCipherContext *context, const guint8 data[], size_t len, guint8 output[], size_t *outlen);
+
+/**
+ * Sets the salt on a context
+ *
+ * @param context The context who's salt to set
+ * @param salt    The salt
+ */
+void gaim_cipher_context_set_salt(GaimCipherContext *context, guint8 *salt);
+
+/**
+ * Gets the size of the salt if the cipher supports it
+ *
+ * @param context The context who's salt size to get
+ *
+ * @return The size of the salt
+ */
+size_t gaim_cipher_context_get_salt_size(GaimCipherContext *context);
+
+/**
+ * Sets the key on a context
+ *
+ * @param context The context who's key to set
+ * @param key     The key
+ */
+void gaim_cipher_context_set_key(GaimCipherContext *context, guint8 *key);
+
+/**
+ * Gets the key size for a context
+ *
+ * @param context The context who's key size to get
+ *
+ * @return The size of the key
+ */
+size_t gaim_cipher_context_get_key_size(GaimCipherContext *context);
+
+/**
+ * Sets the cipher data for a context
+ *
+ * @param context The context who's cipher data to set
+ * @param data    The cipher data to set
+ */
+void gaim_cipher_context_set_data(GaimCipherContext *context, gpointer data);
+
+/**
+ * Gets the cipher data for a context
+ *
+ * @param context The context who's cipher data to get
+ *
+ * @return The cipher data
+ */
+gpointer gaim_cipher_context_get_data(GaimCipherContext *context);
+
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* GAIM_CIPHER_H */
--- a/src/core.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/core.c	Fri Mar 11 13:05:31 2005 +0000
@@ -23,6 +23,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include "internal.h"
+#include "cipher.h"
 #include "connection.h"
 #include "conversation.h"
 #include "core.h"
@@ -96,6 +97,7 @@
 	gaim_status_init();
 	gaim_savedstatuses_init();
 	gaim_accounts_init();
+	gaim_ciphers_init();
 	gaim_connections_init();
 	gaim_conversations_init();
 	gaim_debug_init();
@@ -133,6 +135,7 @@
 	gaim_ssl_uninit();
 	gaim_pounces_uninit();
 	gaim_blist_uninit();
+	gaim_ciphers_uninit();
 	gaim_conversations_uninit();
 	gaim_connections_uninit();
 	gaim_buddy_icons_uninit();
--- a/src/protocols/jabber/auth.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/jabber/auth.c	Fri Mar 11 13:05:31 2005 +0000
@@ -25,11 +25,10 @@
 #include "xmlnode.h"
 #include "jabber.h"
 #include "iq.h"
-#include "sha.h"
 
 #include "debug.h"
-#include "md5.h"
 #include "util.h"
+#include "cipher.h"
 #include "sslconn.h"
 #include "request.h"
 
@@ -227,7 +226,10 @@
 
 			x = xmlnode_new_child(query, "digest");
 			s = g_strdup_printf("%s%s", js->stream_id, pw);
-			shaBlock((unsigned char *)s, strlen(s), hashval);
+
+			gaim_cipher_digest_region("sha1", (guint8 *)s, strlen(s),
+									  hashval, NULL);
+
 			p = h;
 			for(i=0; i<20; i++, p+=2)
 				snprintf(p, 3, "%02x", hashval[i]);
@@ -300,8 +302,9 @@
 generate_response_value(JabberID *jid, const char *passwd, const char *nonce,
 		const char *cnonce, const char *a2, const char *realm)
 {
-	md5_state_t ctx;
-	md5_byte_t result[16];
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	guint8 result[16];
 	size_t a1len;
 
 	unsigned char *x, *a1, *ha1, *ha2, *kd, *z, *convnode, *convpasswd;
@@ -315,32 +318,35 @@
 		convpasswd = g_strdup(passwd);
 	}
 
+	cipher = gaim_ciphers_find_cipher("md5");
+	context = gaim_cipher_context_new(cipher, NULL);
+
 	x = g_strdup_printf("%s:%s:%s", convnode, realm, convpasswd);
-	md5_init(&ctx);
-	md5_append(&ctx, x, strlen(x));
-	md5_finish(&ctx, result);
+	gaim_cipher_context_append(context, x, strlen(x));
+	gaim_cipher_context_digest(context, NULL, result);
 
 	a1 = g_strdup_printf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce);
 	a1len = strlen(a1);
 	g_memmove(a1, result, 16);
 
-	md5_init(&ctx);
-	md5_append(&ctx, a1, a1len);
-	md5_finish(&ctx, result);
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, a1, a1len);
+	gaim_cipher_context_digest(context, NULL, result);
 
 	ha1 = gaim_base16_encode(result, 16);
 
-	md5_init(&ctx);
-	md5_append(&ctx, a2, strlen(a2));
-	md5_finish(&ctx, result);
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, a2, strlen(a2));
+	gaim_cipher_context_digest(context, NULL, result);
 
 	ha2 = gaim_base16_encode(result, 16);
 
 	kd = g_strdup_printf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2);
 
-	md5_init(&ctx);
-	md5_append(&ctx, kd, strlen(kd));
-	md5_finish(&ctx, result);
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, kd, strlen(kd));
+	gaim_cipher_context_digest(context, NULL, result);
+	gaim_cipher_context_destroy(context);
 
 	z = gaim_base16_encode(result, 16);
 
--- a/src/protocols/jabber/buddy.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/jabber/buddy.c	Fri Mar 11 13:05:31 2005 +0000
@@ -19,6 +19,7 @@
  *
  */
 #include "internal.h"
+#include "cipher.h"
 #include "debug.h"
 #include "imgstore.h"
 #include "prpl.h"
@@ -32,8 +33,6 @@
 #include "jabber.h"
 #include "iq.h"
 #include "presence.h"
-#include "sha.h"
-
 
 void jabber_buddy_free(JabberBuddy *jb)
 {
@@ -393,7 +392,10 @@
 
 				photo = xmlnode_new_child(vc_node, "PHOTO");
 				enc = gaim_base64_encode(avatar_data, avatar_len);
-				shaBlock((unsigned char *)avatar_data, avatar_len, hashval);
+
+				gaim_cipher_digest_region("sha1", (guint8 *)avatar_data,
+										  avatar_len, hashval, NULL);
+
 				p = hash;
 				for(i=0; i<20; i++, p+=2)
 					snprintf(p, 3, "%02x", hashval[i]);
@@ -817,7 +819,8 @@
 				gaim_buddy_icons_set_for_user(js->gc->account, bare_jid,
 						data, size);
 
-				shaBlock((unsigned char *)data, size, hashval);
+				gaim_cipher_digest_region("sha1", (guint8 *)data, size,
+										  hashval, NULL);
 				p = hash;
 				for(i=0; i<20; i++, p+=2)
 					snprintf(p, 3, "%02x", hashval[i]);
--- a/src/protocols/jabber/presence.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/jabber/presence.c	Fri Mar 11 13:05:31 2005 +0000
@@ -20,6 +20,7 @@
  */
 #include "internal.h"
 
+#include "cipher.h"
 #include "debug.h"
 #include "notify.h"
 #include "request.h"
@@ -32,7 +33,6 @@
 #include "presence.h"
 #include "iq.h"
 #include "jutil.h"
-#include "sha.h"
 #include "xmlnode.h"
 
 
@@ -215,7 +215,8 @@
 					char hash[41], *p;
 					int i;
 
-					shaBlock((unsigned char *)data, size, hashval);
+					gaim_cipher_digest_region("sha1", (guint8 *)data, size,
+											  hashval, NULL);
 					p = hash;
 					for(i=0; i<20; i++, p+=2)
 						snprintf(p, 3, "%02x", hashval[i]);
--- a/src/protocols/jabber/si.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/jabber/si.c	Fri Mar 11 13:05:31 2005 +0000
@@ -22,12 +22,11 @@
 #include "blist.h"
 
 #include "internal.h"
+#include "cipher.h"
 #include "debug.h"
 #include "ft.h"
 #include "network.h"
 #include "notify.h"
-#include "sha.h"
-#include "util.h"
 
 #include "buddy.h"
 #include "disco.h"
@@ -155,7 +154,9 @@
 
 	dstaddr = g_strdup_printf("%s%s%s@%s/%s", jsx->stream_id, xfer->who, jsx->js->user->node,
 			jsx->js->user->domain, jsx->js->user->resource);
-	shaBlock((unsigned char *)dstaddr, strlen(dstaddr), hashval);
+
+	gaim_cipher_digest_region("sha1", (guint8 *)dstaddr, strlen(dstaddr),
+							  hashval, NULL);
 	g_free(dstaddr);
 	dstaddr = g_malloc(41);
 	p = dstaddr;
@@ -269,7 +270,9 @@
 	dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
 			jsx->js->user->node, jsx->js->user->domain,
 			jsx->js->user->resource, xfer->who);
-	shaBlock((unsigned char *)dstaddr, strlen(dstaddr), hashval);
+
+	gaim_cipher_digest_region("sha1", (guint8 *)dstaddr, strlen(dstaddr),
+							  hashval, NULL);
 	g_free(dstaddr);
 	dstaddr = g_malloc(41);
 	p = dstaddr;
--- a/src/protocols/msn/msn.h	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/msn/msn.h	Fri Mar 11 13:05:31 2005 +0000
@@ -40,14 +40,13 @@
 #include "connection.h"
 #include "conversation.h"
 #include "debug.h"
-#include "md5.h"
+#include "cipher.h"
 #include "notify.h"
 #include "privacy.h"
 #include "proxy.h"
 #include "prpl.h"
 #include "request.h"
 #include "servconn.h"
-#include "sha.h"
 #include "sslconn.h"
 #include "util.h"
 
--- a/src/protocols/msn/notification.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/msn/notification.c	Fri Mar 11 13:05:31 2005 +0000
@@ -403,22 +403,26 @@
 	MsnTransaction *trans;
 	char buf[33];
 	const char *challenge_resp;
-	md5_state_t st;
-	md5_byte_t di[16];
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	guint8 digest[16];
 	int i;
 
-	md5_init(&st);
-	md5_append(&st, (const md5_byte_t *)cmd->params[1],
-			   strlen(cmd->params[1]));
+	cipher = gaim_ciphers_find_cipher("md5");
+	context = gaim_cipher_context_new(cipher, NULL);
+
+	gaim_cipher_context_append(context, cmd->params[1],
+							   strlen(cmd->params[1]));
 
 	challenge_resp = "VT6PX?UQTM4WM%YR";
 
-	md5_append(&st, (const md5_byte_t *)challenge_resp,
-			   strlen(challenge_resp));
-	md5_finish(&st, di);
+	gaim_cipher_context_append(context, challenge_resp,
+							   strlen(challenge_resp));
+	gaim_cipher_context_digest(context, NULL, digest);
+	gaim_cipher_context_destroy(context);
 
 	for (i = 0; i < 16; i++)
-		g_snprintf(buf + (i*2), 3, "%02x", di[i]);
+		g_snprintf(buf + (i*2), 3, "%02x", digest[i]);
 
 	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9");
 
@@ -882,8 +886,9 @@
 	GaimAccount *account;
 	const char *rru;
 	const char *url;
-	md5_state_t st;
-	md5_byte_t di[16];
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	guint8 digest[16];
 	FILE *fd;
 	char buf[2048];
 	char buf2[3];
@@ -901,15 +906,18 @@
 			   time(NULL) - session->passport_info.sl,
 			   gaim_account_get_password(account));
 
-	md5_init(&st);
-	md5_append(&st, (const md5_byte_t *)buf, strlen(buf));
-	md5_finish(&st, di);
+	cipher = gaim_ciphers_find_cipher("md5");
+	context = gaim_cipher_context_new(cipher, NULL);
+
+	gaim_cipher_context_append(context, buf, strlen(buf));
+	gaim_cipher_context_digest(context, NULL, digest);
+	gaim_cipher_context_destroy(context);
 
 	memset(sendbuf, 0, sizeof(sendbuf));
 
 	for (i = 0; i < 16; i++)
 	{
-		g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
+		g_snprintf(buf2, sizeof(buf2), "%02x", digest[i]);
 		strcat(sendbuf, buf2);
 	}
 
--- a/src/protocols/msn/user.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/msn/user.c	Fri Mar 11 13:05:31 2005 +0000
@@ -176,8 +176,8 @@
 	}
 	else if ((fp = g_fopen(filename, "rb")) != NULL)
 	{
+		GaimCipherContext *ctx;
 		unsigned char *buf;
-		SHA_CTX ctx;
 		gsize len;
 		unsigned char *base64;
 		unsigned char digest[20];
@@ -203,9 +203,9 @@
 		/* Compute the SHA1D field. */
 		memset(digest, 0, sizeof(digest));
 
-		shaInit(&ctx);
-		shaUpdate(&ctx, buf, st.st_size);
-		shaFinal(&ctx, digest);
+		ctx = gaim_cipher_context_new_by_name("sha1", NULL);
+		gaim_cipher_context_append(ctx, buf, st.st_size);
+		gaim_cipher_context_digest(ctx, NULL, digest);
 		g_free(buf);
 
 		base64 = gaim_base64_encode(digest, sizeof(digest));
@@ -226,9 +226,10 @@
 
 		memset(digest, 0, sizeof(digest));
 
-		shaInit(&ctx);
-		shaUpdate(&ctx, buf, strlen(buf));
-		shaFinal(&ctx, digest);
+		gaim_cipher_context_reset(ctx, NULL);
+		gaim_cipher_context_append(ctx, buf, strlen(buf));
+		gaim_cipher_context_digest(ctx, NULL, digest);
+		gaim_cipher_context_destroy(ctx);
 		g_free(buf);
 
 		base64 = gaim_base64_encode(digest, sizeof(digest));
--- a/src/protocols/oscar/Makefile.am	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/oscar/Makefile.am	Fri Mar 11 13:05:31 2005 +0000
@@ -29,6 +29,7 @@
 	im.c           \
 	invite.c       \
 	locate.c       \
+	md5.c          \
 	misc.c         \
 	msgcookie.c    \
 	odir.c         \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/md5.c	Fri Mar 11 13:05:31 2005 +0000
@@ -0,0 +1,392 @@
+/*
+  Copyright (C) 1999 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321.
+  It is derived directly from the text of the RFC and not from the
+  reference implementation.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+#include <string.h>
+main()
+{
+    static const char *const test[7] = {
+	"", /*d41d8cd98f00b204e9800998ecf8427e*/
+	"945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/
+	"abc", /*900150983cd24fb0d6963f7d28e17f72*/
+	"message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+	"abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+				/*d174ab98d277d9f5a5611c2c9f419d9f*/
+	"12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+    };
+    int i;
+
+    for (i = 0; i < 7; ++i) {
+	md5_state_t state;
+	md5_byte_t digest[16];
+	int di;
+
+	md5_init(&state);
+	md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+	md5_finish(&state, digest);
+	printf("MD5 (\"%s\") = ", test[i]);
+	for (di = 0; di < 16; ++di)
+	    printf("%02x", digest[di]);
+	printf("\n");
+    }
+    return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+    int i;
+    for (i = 1; i <= 64; ++i) {
+	unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+	printf("#define T%d 0x%08lx\n", i, v);
+    }
+    return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1	/* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+    /*
+     * On big-endian machines, we must arrange the bytes in the right
+     * order.  (This also works on machines of unknown byte order.)
+     */
+    md5_word_t X[16];
+    const md5_byte_t *xp = data;
+    int i;
+
+    for (i = 0; i < 16; ++i, xp += 4)
+	X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else  /* !ARCH_IS_BIG_ENDIAN */
+
+    /*
+     * On little-endian machines, we can process properly aligned data
+     * without copying it.
+     */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+
+    if (!((data - (const md5_byte_t *)0) & 3)) {
+	/* data are properly aligned */
+	X = (const md5_word_t *)data;
+    } else {
+	/* not aligned */
+	memcpy(xbuf, data, 64);
+	X = xbuf;
+    }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = 0xefcdab89;
+    pms->abcd[2] = 0x98badcfe;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/md5.h	Fri Mar 11 13:05:31 2005 +0000
@@ -0,0 +1,93 @@
+/*
+  Copyright (C) 1999 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321.
+  It is derived directly from the text of the RFC and not from the
+  reference implementation.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+	added conditionalization for C++ compilation from Martin
+	Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints.  Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/* message length in bits, lsw first */
+    md5_word_t abcd[4];		/* digest buffer */
+    md5_byte_t buf[64];		/* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+#ifdef P1
+void md5_init(P1(md5_state_t *pms));
+#else
+void md5_init(md5_state_t *pms);
+#endif
+
+/* Append a string to the message. */
+#ifdef P3
+void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes));
+#else
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+#endif
+
+/* Finish the message and return the digest. */
+#ifdef P2
+void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16]));
+#else
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#endif
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
--- a/src/protocols/yahoo/crypt.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/yahoo/crypt.c	Fri Mar 11 13:05:31 2005 +0000
@@ -26,7 +26,7 @@
 #include <stdlib.h>
 #include <glib.h>
 
-#include "md5.h"
+#include "cipher.h"
 
 /* Define our magic string to mark salt for MD5 "encryption"
    replacement.  This is meant to be the same as for other MD5 based
@@ -39,13 +39,13 @@
 
 char *yahoo_crypt(const char *key, const char *salt)
 {
+	GaimCipher *cipher;
+	GaimCipherContext *context1, *context2;
+	guint8 digest[16];
 	static char *buffer = NULL;
 	static int buflen = 0;
 	int needed = 3 + strlen (salt) + 1 + 26 + 1;
 
-	md5_byte_t alt_result[16];
-	md5_state_t ctx;
-	md5_state_t alt_ctx;
 	size_t salt_len;
 	size_t key_len;
 	size_t cnt;
@@ -57,8 +57,13 @@
 			return NULL;
 	}
 
+	cipher = gaim_ciphers_find_cipher("md5");
+	context1 = gaim_cipher_context_new(cipher, NULL);
+	context2 = gaim_cipher_context_new(cipher, NULL);
+
 	/* Find beginning of salt string.  The prefix should normally always
-	   be present.  Just in case it is not.  */
+	 * be present.  Just in case it is not.
+	 */
 	if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
 		/* Skip salt prefix.  */
 		salt += sizeof (md5_salt_prefix) - 1;
@@ -66,90 +71,90 @@
 	salt_len = MIN (strcspn (salt, "$"), 8);
 	key_len = strlen (key);
 
-	/* Prepare for the real work.  */
-	md5_init(&ctx);
-
 	/* Add the key string.  */
-	md5_append(&ctx, key, key_len);
+	gaim_cipher_context_append(context1, key, key_len);
 
 	/* Because the SALT argument need not always have the salt prefix we
-	   add it separately.  */
-	md5_append(&ctx, md5_salt_prefix, sizeof (md5_salt_prefix) - 1);
+	 * add it separately.
+	 */
+	gaim_cipher_context_append(context1, md5_salt_prefix,
+							   sizeof(md5_salt_prefix) - 1);
 
 	/* The last part is the salt string.  This must be at most 8
-	   characters and it ends at the first `$' character (for
-	   compatibility which existing solutions).  */
-	md5_append(&ctx, salt, salt_len);
+	 * characters and it ends at the first `$' character (for
+	 * compatibility which existing solutions).
+	 */
+	gaim_cipher_context_append(context1, salt, salt_len);
 
 	/* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
-	   final result will be added to the first context.  */
-	md5_init(&alt_ctx);
+	 * final result will be added to the first context.
+	 */
 
 	/* Add key.  */
-	md5_append(&alt_ctx, key, key_len);
+	gaim_cipher_context_append(context2, key, key_len);
 
 	/* Add salt.  */
-	md5_append(&alt_ctx, salt, salt_len);
+	gaim_cipher_context_append(context2, salt, salt_len);
 
 	/* Add key again.  */
-	md5_append(&alt_ctx, key, key_len);
+	gaim_cipher_context_append(context2, key, key_len);
 
-	/* Now get result of this (16 bytes) and add it to the other
-	   context.  */
-	md5_finish(&alt_ctx, alt_result);
+	/* Now get result of this (16 bytes) and add it to the other context.  */
+	gaim_cipher_context_digest(context2, NULL, digest);
 
 	/* Add for any character in the key one byte of the alternate sum.  */
 	for (cnt = key_len; cnt > 16; cnt -= 16)
-		md5_append(&ctx, alt_result, 16);
-	md5_append(&ctx, alt_result, cnt);
+		gaim_cipher_context_append(context1, digest, 16);
+	gaim_cipher_context_append(context1, digest, cnt);
 
 	/* For the following code we need a NUL byte.  */
-	alt_result[0] = '\0';
+	digest[0] = '\0';
 
 	/* The original implementation now does something weird: for every 1
-	   bit in the key the first 0 is added to the buffer, for every 0
-	   bit the first character of the key.  This does not seem to be
-	   what was intended but we have to follow this to be compatible.  */
+	 * bit in the key the first 0 is added to the buffer, for every 0
+	 * bit the first character of the key.  This does not seem to be
+	 * what was intended but we have to follow this to be compatible.
+	 */
 	for (cnt = key_len; cnt > 0; cnt >>= 1)
-		md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
+		gaim_cipher_context_append(context1,
+								   (cnt & 1) != 0 ? digest : (guint8 *)key, 1);
 
 	/* Create intermediate result.  */
-	md5_finish(&ctx, alt_result);
+	gaim_cipher_context_digest(context1, NULL, digest);
 
 	/* Now comes another weirdness.  In fear of password crackers here
-	   comes a quite long loop which just processes the output of the
-	   previous round again.  We cannot ignore this here.  */
+	 * comes a quite long loop which just processes the output of the
+	 * previous round again.  We cannot ignore this here.
+	 */
 	for (cnt = 0; cnt < 1000; ++cnt) {
 		/* New context.  */
-		md5_init(&ctx);
+		gaim_cipher_context_reset(context2, NULL);
 
 		/* Add key or last result.  */
 		if ((cnt & 1) != 0)
-			md5_append(&ctx, key, key_len);
+			gaim_cipher_context_append(context2, key, key_len);
 		else
-			md5_append(&ctx, alt_result, 16);
+			gaim_cipher_context_append(context2, digest, 16);
 
 		/* Add salt for numbers not divisible by 3.  */
 		if (cnt % 3 != 0)
-			md5_append(&ctx, salt, salt_len);
+			gaim_cipher_context_append(context2, salt, salt_len);
 
 		/* Add key for numbers not divisible by 7.  */
 		if (cnt % 7 != 0)
-			md5_append(&ctx, key, key_len);
+			gaim_cipher_context_append(context2, key, key_len);
 
 		/* Add key or last result.  */
 		if ((cnt & 1) != 0)
-			md5_append(&ctx, alt_result, 16);
+			gaim_cipher_context_append(context2, digest, 16);
 		else
-			md5_append(&ctx, key, key_len);
+			gaim_cipher_context_append(context2, key, key_len);
 
 		/* Create intermediate result.  */
-		md5_finish(&ctx, alt_result);
+		gaim_cipher_context_digest(context2, NULL, digest);
 	}
 
-	/* Now we can construct the result string.  It consists of three
-	   parts.  */
-
+	/* Now we can construct the result string.  It consists of three parts. */
 	strncpy(buffer, md5_salt_prefix, MAX (0, buflen));
 	cp = buffer + strlen(buffer);
 	buflen -= sizeof (md5_salt_prefix);
@@ -174,12 +179,12 @@
 		}\
 	} while (0)
 
-	b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
-	b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
-	b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
-	b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
-	b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
-	b64_from_24bit (0, 0, alt_result[11], 2);
+	b64_from_24bit (digest[0], digest[6], digest[12], 4);
+	b64_from_24bit (digest[1], digest[7], digest[13], 4);
+	b64_from_24bit (digest[2], digest[8], digest[14], 4);
+	b64_from_24bit (digest[3], digest[9], digest[15], 4);
+	b64_from_24bit (digest[4], digest[10], digest[5], 4);
+	b64_from_24bit (0, 0, digest[11], 2);
 	if (buflen <= 0) {
 		g_free(buffer);
 		buffer = NULL;
@@ -187,13 +192,14 @@
 		*cp = '\0';	/* Terminate the string.  */
 
 	/* Clear the buffer for the intermediate result so that people
-	   attaching to processes or reading core dumps cannot get any
-	   information.  We do it in this way to clear correct_words[]
-	   inside the MD5 implementation as well.  */
-	md5_init(&ctx);
-	md5_finish(&ctx, alt_result);
-	memset (&ctx, '\0', sizeof (ctx));
-	memset (&alt_ctx, '\0', sizeof (alt_ctx));
+	 * attaching to processes or reading core dumps cannot get any
+	 * information.  We do it in this way to clear correct_words[]
+	 * inside the MD5 implementation as well.
+	 */
+	gaim_cipher_context_reset(context1, NULL);
+	gaim_cipher_context_digest(context1, NULL, digest);
+	gaim_cipher_context_destroy(context1);
+	gaim_cipher_context_destroy(context2);
 
 	return buffer;
 }
--- a/src/protocols/yahoo/yahoo.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/protocols/yahoo/yahoo.c	Fri Mar 11 13:05:31 2005 +0000
@@ -26,6 +26,7 @@
 #include "account.h"
 #include "accountopt.h"
 #include "blist.h"
+#include "cipher.h"
 #include "cmds.h"
 #include "debug.h"
 #include "notify.h"
@@ -37,7 +38,6 @@
 #include "util.h"
 #include "version.h"
 
-#include "sha.h"
 #include "yahoo.h"
 #include "yahoo_packet.h"
 #include "yahoo_friend.h"
@@ -46,7 +46,6 @@
 #include "yahoo_auth.h"
 #include "yahoo_filexfer.h"
 #include "yahoo_picture.h"
-#include "md5.h"
 
 extern char *yahoo_crypt(const char *, const char *);
 
@@ -930,13 +929,14 @@
 	 * The new clients use this authentication method.  I warn you in advance, it's
 	 * bizarre, convoluted, inordinately complicated.  It's also no more secure than
 	 * crypt() was.  The only purpose this scheme could serve is to prevent third
-	 * part clients from connecting to their servers.
+	 * party clients from connecting to their servers.
 	 *
 	 * Sorry, Yahoo.
 	 */
 
-	md5_byte_t result[16];
-	md5_state_t ctx;
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	guint8 digest[16];
 
 	char *crypt_result;
 	char password_hash[25];
@@ -954,16 +954,20 @@
 	sv = seed[15];
 	sv = sv % 8;
 
-	md5_init(&ctx);
-	md5_append(&ctx, pass, strlen(pass));
-	md5_finish(&ctx, result);
-	to_y64(password_hash, result, 16);
-
-	md5_init(&ctx);
+	cipher = gaim_ciphers_find_cipher("md5");
+	context = gaim_cipher_context_new(cipher, NULL);
+
+	gaim_cipher_context_append(context, pass, strlen(pass));
+	gaim_cipher_context_digest(context, NULL, digest);
+
+	to_y64(password_hash, digest, 16);
+
 	crypt_result = yahoo_crypt(pass, "$1$_2S43d5f$");
-	md5_append(&ctx, crypt_result, strlen(crypt_result));
-	md5_finish(&ctx, result);
-	to_y64(crypt_hash, result, 16);
+
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, crypt_result, strlen(crypt_result));
+	gaim_cipher_context_digest(context, NULL, digest);
+	to_y64(crypt_hash, digest, 16);
 
 	switch (sv) {
 	case 1:
@@ -1006,15 +1010,16 @@
 			break;
 	}
 
-	md5_init(&ctx);
-	md5_append(&ctx, hash_string_p, strlen(hash_string_p));
-	md5_finish(&ctx, result);
-	to_y64(result6, result, 16);
-
-	md5_init(&ctx);
-	md5_append(&ctx, hash_string_c, strlen(hash_string_c));
-	md5_finish(&ctx, result);
-	to_y64(result96, result, 16);
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, hash_string_p, strlen(hash_string_p));
+	gaim_cipher_context_digest(context, NULL, digest);
+	to_y64(result6, digest, 16);
+
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, hash_string_c, strlen(hash_string_c));
+	gaim_cipher_context_digest(context, NULL, digest);
+	gaim_cipher_context_destroy(context);
+	to_y64(result96, digest, 16);
 
 	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP,	YAHOO_STATUS_AVAILABLE, 0);
 	yahoo_packet_hash(pack, "ssss", 0, name, 6, result6, 96, result96, 1, name);
@@ -1035,11 +1040,13 @@
 	const char *pass = gaim_account_get_password(account);
 	struct yahoo_data *yd = gc->proto_data;
 
-	md5_byte_t			result[16];
-	md5_state_t			ctx;
-
-	SHA_CTX				ctx1;
-	SHA_CTX				ctx2;
+	GaimCipher 			*md5_cipher;
+	GaimCipherContext	*md5_ctx;
+	guint8				md5_digest[16];
+
+	GaimCipher			*sha1_cipher;
+	GaimCipherContext	*sha1_ctx1;
+	GaimCipherContext	*sha1_ctx2;
 
 	char				*alphabet1			= "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ";
 	char				*alphabet2			= "F0E1D2C3B4A59687abcdefghijklmnop";
@@ -1089,6 +1096,13 @@
 	memset(&magic_key_char, 0, 4);
 	memset(&comparison_src, 0, 20);
 
+	md5_cipher = gaim_ciphers_find_cipher("md5");
+	md5_ctx = gaim_cipher_context_new(md5_cipher, NULL);
+
+	sha1_cipher = gaim_ciphers_find_cipher("sha1");
+	sha1_ctx1 = gaim_cipher_context_new(sha1_cipher, NULL);
+	sha1_ctx2 = gaim_cipher_context_new(sha1_cipher, NULL);
+
 	/* 
 	 * Magic: Phase 1.  Generate what seems to be a 30 byte value (could change if base64
 	 * ends up differently?  I don't remember and I'm tired, so use a 64 byte buffer.
@@ -1208,42 +1222,31 @@
 		comparison_src[x++] = bl & 0xff; 
 	} while (x < 20);
 	
-	/* First four bytes are magic key.
-	 */
-	
+	/* First four bytes are magic key. */
 	memcpy(&magic_key_char[0], comparison_src, 4);
 	magic_4 = magic_key_char[0] | (magic_key_char[1]<<8) | (magic_key_char[2]<<16) | (magic_key_char[3]<<24);
-	
+
 	/* 
 	 * Magic: Phase 4.  Determine what function to use later by getting outside/inside
 	 * loop values until we match our previous buffer.
 	 */
-	
 	for (x = 0; x < 65535; x++) {
 		int			leave = 0;
 
 		for (y = 0; y < 5; y++) {
-			md5_byte_t		result[16];
-			md5_state_t		ctx;
-			
 			unsigned char	test[3];
-			
-			memset(&result, 0, 16);
-			memset(&test, 0, 3);
-			
-			/* Calculate buffer.
-			 */
-
+
+			/* Calculate buffer. */
 			test[0] = x;
 			test[1] = x >> 8;
 			test[2] = y;
 			
-			md5_init(&ctx);
-			md5_append(&ctx, magic_key_char, 4);
-			md5_append(&ctx, test, 3);
-			md5_finish(&ctx, result);
+			gaim_cipher_context_reset(md5_ctx, NULL);
+			gaim_cipher_context_append(md5_ctx, magic_key_char, 4);
+			gaim_cipher_context_append(md5_ctx, test, 3);
+			gaim_cipher_context_digest(md5_ctx, NULL, md5_digest);
 			
-			if (!memcmp(result, comparison_src+4, 16)) {
+			if (!memcmp(md5_digest, comparison_src+4, 16)) {
 				leave = 1;
 				break;
 			}
@@ -1253,41 +1256,35 @@
 			break;
 	}
 	
-	/* If y != 0, we need some help.
-	 */
-	
+	/* If y != 0, we need some help. */
 	if (y != 0) {
 		unsigned int	updated_key;
-		
-		/* Update magic stuff.   Call it twice because Yahoo's encryption is super bad ass.
+
+		/* Update magic stuff.
+		 * Call it twice because Yahoo's encryption is super bad ass.
 		 */
-		
 		updated_key = yahoo_auth_finalCountdown(magic_4, 0x60, y, x);
 		updated_key = yahoo_auth_finalCountdown(updated_key, 0x60, y, x);
-		
+
 		magic_key_char[0] = updated_key & 0xff;
 		magic_key_char[1] = (updated_key >> 8) & 0xff;
 		magic_key_char[2] = (updated_key >> 16) & 0xff;
 		magic_key_char[3] = (updated_key >> 24) & 0xff;
 	} 
 	
-/* Get password and crypt hashes as per usual.
-	 */
-	
-	md5_init(&ctx);
-	md5_append(&ctx, pass, strlen(pass));
-	md5_finish(&ctx, result);
-	to_y64(password_hash, result, 16);
-	
-	md5_init(&ctx);
+	/* Get password and crypt hashes as per usual. */
+	gaim_cipher_context_reset(md5_ctx, NULL);
+	gaim_cipher_context_append(md5_ctx, pass, strlen(pass));
+	gaim_cipher_context_digest(md5_ctx, NULL, md5_digest);
+	to_y64(password_hash, md5_digest, 16);
+
 	crypt_result = yahoo_crypt(pass, "$1$_2S43d5f$");  
-	md5_append(&ctx, crypt_result, strlen(crypt_result));
-	md5_finish(&ctx, result);
-	to_y64(crypt_hash, result, 16);
-
-	/* Our first authentication response is based off of the password hash.
-	 */
-	
+	gaim_cipher_context_reset(md5_ctx, NULL);
+	gaim_cipher_context_append(md5_ctx, crypt_result, strlen(crypt_result));
+	gaim_cipher_context_digest(md5_ctx, NULL, md5_digest);
+	to_y64(crypt_hash, md5_digest, 16);
+
+	/* Our first authentication response is based off of the password hash. */
 	for (x = 0; x < (int)strlen(password_hash); x++) 
 		pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36;
 	
@@ -1302,28 +1299,25 @@
 	if (cnt < 64) 
 		memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt);
 	
-	shaInit(&ctx1);
-	shaInit(&ctx2);
-	
 	/* 
 	 * The first context gets the password hash XORed with 0x36 plus a magic value
 	 * which we previously extrapolated from our challenge.
 	 */
 	
-	shaUpdate(&ctx1, pass_hash_xor1, 64);
+	gaim_cipher_context_append(sha1_ctx1, pass_hash_xor1, 64);
 	if (y >= 3)
-		ctx1.sizeLo = 0x1ff;
-	shaUpdate(&ctx1, magic_key_char, 4);
-	shaFinal(&ctx1, digest1);
+		gaim_cipher_context_set_option(sha1_ctx1, "sizeLo", GINT_TO_POINTER(0x1ff));
+	gaim_cipher_context_append(sha1_ctx1, magic_key_char, 4);
+	gaim_cipher_context_digest(sha1_ctx1, NULL, digest1);
 	
 	/* 
 	 * The second context gets the password hash XORed with 0x5c plus the SHA-1 digest
 	 * of the first context.
 	 */
 	
-	shaUpdate(&ctx2, pass_hash_xor2, 64);
-	shaUpdate(&ctx2, digest1, 20);
-	shaFinal(&ctx2, digest2);
+	gaim_cipher_context_append(sha1_ctx2, pass_hash_xor2, 64);
+	gaim_cipher_context_append(sha1_ctx2, digest1, 20);
+	gaim_cipher_context_digest(sha1_ctx2, NULL, digest2);
 	
 	/* 
 	 * Now that we have digest2, use it to fetch characters from an alphabet to construct
@@ -1395,28 +1389,28 @@
 	if (cnt < 64) 
 		memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt);
 	
-	shaInit(&ctx1);
-	shaInit(&ctx2);
+	gaim_cipher_context_reset(sha1_ctx1, NULL);
+	gaim_cipher_context_reset(sha1_ctx2, NULL);
 	
 	/* 
 	 * The first context gets the password hash XORed with 0x36 plus a magic value
 	 * which we previously extrapolated from our challenge.
 	 */
 	
-	shaUpdate(&ctx1, crypt_hash_xor1, 64);
+	gaim_cipher_context_append(sha1_ctx1, crypt_hash_xor1, 64);
 	if (y >= 3)
-		ctx1.sizeLo = 0x1ff;
-	shaUpdate(&ctx1, magic_key_char, 4);
-	shaFinal(&ctx1, digest1);
+		gaim_cipher_context_set_option(sha1_ctx1, "sizeLo", GINT_TO_POINTER(0x1ff));
+	gaim_cipher_context_append(sha1_ctx1, magic_key_char, 4);
+	gaim_cipher_context_digest(sha1_ctx1, NULL, digest1);
 	
 	/* 
 	 * The second context gets the password hash XORed with 0x5c plus the SHA-1 digest
 	 * of the first context.
 	 */
 	
-	shaUpdate(&ctx2, crypt_hash_xor2, 64);
-	shaUpdate(&ctx2, digest1, 20);
-	shaFinal(&ctx2, digest2);
+	gaim_cipher_context_append(sha1_ctx2, crypt_hash_xor2, 64);
+	gaim_cipher_context_append(sha1_ctx2, digest1, 20);
+	gaim_cipher_context_digest(sha1_ctx2, NULL, digest2);
 	
 	/* 
 	 * Now that we have digest2, use it to fetch characters from an alphabet to construct
@@ -1475,6 +1469,10 @@
 	
 	yahoo_packet_send_and_free(pack, yd);
 
+	gaim_cipher_context_destroy(md5_ctx);
+	gaim_cipher_context_destroy(sha1_ctx1);
+	gaim_cipher_context_destroy(sha1_ctx2);
+
 	g_free(password_hash);
 	g_free(crypt_hash);
 }
@@ -2174,35 +2172,42 @@
 	GString *url = g_string_new("GET http://login.yahoo.com/config/login?login=");
 	char md5[33], *hashp = md5, *chal;
 	int i;
-	md5_byte_t result[16];
-	md5_state_t ctx;
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	guint8 digest[16];
 
 	url = g_string_append(url, sn);
 	url = g_string_append(url, "&passwd=");
 
-	md5_init(&ctx);
-	md5_append(&ctx, pass, strlen(pass));
-	md5_finish(&ctx, result);
+	cipher = gaim_ciphers_find_cipher("md5");
+	context = gaim_cipher_context_new(cipher, NULL);
+
+	gaim_cipher_context_append(context, pass, strlen(pass));
+	gaim_cipher_context_digest(context, NULL, digest);
 	for (i = 0; i < 16; ++i) {
-		g_snprintf(hashp, 3, "%02x", result[i]);
+		g_snprintf(hashp, 3, "%02x", digest[i]);
 		hashp += 2;
 	}
+
 	chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
-	md5_init(&ctx);
-	md5_append(&ctx, chal, strlen(chal));
-	md5_finish(&ctx, result);
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, chal, strlen(chal));
+	gaim_cipher_context_digest(context, NULL, digest);
 	hashp = md5;
 	for (i = 0; i < 16; ++i) {
-		g_snprintf(hashp, 3, "%02x", result[i]);
+		g_snprintf(hashp, 3, "%02x", digest[i]);
 		hashp += 2;
 	}
 	/*
-	md5_init(&ctx);
-	md5_append(&ctx, md5, strlen(md5));
-	md5_finish(&ctx, result);
+	 * I dunno why this is here and commented out.. but in case it's needed
+	 * I updated it..
+
+	gaim_cipher_context_reset(context, NULL);
+	gaim_cipher_context_append(context, md5, strlen(md5));
+	gaim_cipher_context_digest(context, NULL, digest);
 	hashp = md5;
 	for (i = 0; i < 16; ++i) {
-		g_snprintf(hashp, 3, "%02x", result[i]);
+		g_snprintf(hashp, 3, "%02x", digest[i]);
 		hashp += 2;
 	}
 	*/
@@ -2219,6 +2224,8 @@
 		gaim_connection_error(gc, _("Connection problem"));
 		return;
 	}
+
+	gaim_cipher_context_destroy(context);
 }
 
 static void yahoo_server_check(GaimAccount *account)
--- a/src/proxy.c	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/proxy.c	Fri Mar 11 13:05:31 2005 +0000
@@ -30,12 +30,12 @@
  , 3rd draw women to it like flies to honey */
 
 #include "internal.h"
+#include "cipher.h"
 #include "debug.h"
 #include "notify.h"
 #include "prefs.h"
 #include "proxy.h"
 #include "util.h"
-#include "md5.h"
 
 static GaimProxyInfo *global_proxy_info = NULL;
 
@@ -1361,20 +1361,23 @@
 
 static void hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
 {
+	GaimCipher *cipher;
+	GaimCipherContext *ctx;
 	int i;
 	unsigned char Kxoripad[65];
 	unsigned char Kxoropad[65];
-	md5_state_t ctx;
 	int pwlen;
 	char * pwinput;
 	char md5buf[16];
 
+	cipher = gaim_ciphers_find_cipher("md5");
+	ctx = gaim_cipher_context_new(cipher, NULL);
+
 	pwinput=(char *)passwd;
 	pwlen=strlen(passwd);
 	if (pwlen>64) {
-		md5_init(&ctx);
-		md5_append(&ctx, (unsigned char *)passwd, strlen(passwd));
-		md5_finish(&ctx, (unsigned char *)md5buf);
+		gaim_cipher_context_append(ctx, passwd, strlen(passwd));
+		gaim_cipher_context_digest(ctx, NULL, md5buf);
 		pwinput=(char *)md5buf;
 		pwlen=16;
 	}
@@ -1387,15 +1390,18 @@
 		Kxoripad[i]^=0x36;
 		Kxoropad[i]^=0x5c;
 	}
-	md5_init(&ctx);
-	md5_append(&ctx, Kxoripad, 64);
-	md5_append(&ctx, challenge, challen);
-	md5_finish(&ctx, (unsigned char *)Kxoripad);
+
+	gaim_cipher_context_reset(ctx, NULL);
+	gaim_cipher_context_append(ctx, Kxoripad, 64);
+	gaim_cipher_context_append(ctx, challenge, challen);
+	gaim_cipher_context_digest(ctx, NULL, Kxoripad);
 
-	md5_init(&ctx);
-	md5_append(&ctx, Kxoropad, 64);
-	md5_append(&ctx, Kxoripad, 16);
-	md5_finish(&ctx, response);
+	gaim_cipher_context_reset(ctx, NULL);
+	gaim_cipher_context_append(ctx, Kxoropad, 64);
+	gaim_cipher_context_append(ctx, Kxoripad, 16);
+	gaim_cipher_context_digest(ctx, NULL, response);
+
+	gaim_cipher_context_destroy(ctx);
 }
 
 static void
--- a/src/value.h	Fri Mar 11 04:13:27 2005 +0000
+++ b/src/value.h	Fri Mar 11 13:05:31 2005 +0000
@@ -68,7 +68,8 @@
 	GAIM_SUBTYPE_CONVERSATION,
 	GAIM_SUBTYPE_CONV_WINDOW,
 	GAIM_SUBTYPE_PLUGIN,
-	GAIM_SUBTYPE_BLIST_NODE
+	GAIM_SUBTYPE_BLIST_NODE,
+	GAIM_SUBTYPE_CIPHER
 
 } GaimSubType;