diff --git a/indra/llcommon/llbase64.cpp b/indra/llcommon/llbase64.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..88e9d960a26baf97aef64e8696d7c44edcae13ad
--- /dev/null
+++ b/indra/llcommon/llbase64.cpp
@@ -0,0 +1,43 @@
+/** 
+ * @file llbase64.cpp
+ * @brief Wrapper for apr base64 encoding that returns a std::string
+ * @author James Cook
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llbase64.h"
+
+#include <string>
+
+#include "apr-1/apr_base64.h"
+
+
+// static
+std::string LLBase64::encode(const U8* input, size_t input_size)
+{
+	std::string output;
+	if (input
+		&& input_size > 0)
+	{
+		// Yes, it returns int.
+		int b64_buffer_length = apr_base64_encode_len(input_size);
+		char* b64_buffer = new char[b64_buffer_length];
+		
+		// This is faster than apr_base64_encode() if you know
+		// you're not on an EBCDIC machine.  Also, the output is
+		// null terminated, even though the documentation doesn't
+		// specify.  See apr_base64.c for details. JC
+		b64_buffer_length = apr_base64_encode_binary(
+			b64_buffer,
+			input,
+			input_size);
+		output.assign(b64_buffer);
+		delete[] b64_buffer;
+	}
+	return output;
+}
+
diff --git a/indra/llcommon/llbase64.h b/indra/llcommon/llbase64.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e8b817ba73e8ea91666e360263cca757382b20b
--- /dev/null
+++ b/indra/llcommon/llbase64.h
@@ -0,0 +1,19 @@
+/** 
+ * @file llbase64.h
+ * @brief Wrapper for apr base64 encoding that returns a std::string
+ * @author James Cook
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLBASE64_H
+#define LLBASE64_h
+
+class LLBase64
+{
+public:
+	static std::string encode(const U8* input, size_t input_size);
+};
+
+#endif
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
index c21b8f19a4a592c8fbb5a08a6a75af44fbc2d140..796cd9f9f498b53a529aa25ed49b91ed51e71a18 100644
--- a/indra/llcommon/llsdserialize_xml.cpp
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -171,6 +171,7 @@ S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 opti
 		else
 		{
 			// *FIX: memory inefficient.
+			// *TODO: convert to use LLBase64
 			ostr << pre << "<binary encoding=\"base64\">";
 			int b64_buffer_length = apr_base64_encode_len(buffer.size());
 			char* b64_buffer = new char[b64_buffer_length];
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index a381af74d03cf9696bea802d84526b20abc899bb..6e9ea0b5a0c0b5b9453a85e20fa51378dff717f2 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -904,7 +904,7 @@ void LLStringBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T r
 
 //static
 template<class T> 
-void LLStringBase<T>::replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab )
+void LLStringBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
 {
 	llassert( spaces_per_tab >= 0 );
 
@@ -913,19 +913,19 @@ void LLStringBase<T>::replaceTabsWithSpaces( std::basic_string<T>& string, size_
 
 	LLStringBase<T> out_str;
 	// Replace tabs with spaces
-	for (size_type i = 0; i < string.length(); i++)
+	for (size_type i = 0; i < str.length(); i++)
 	{
-		if (string[i] == TAB)
+		if (str[i] == TAB)
 		{
 			for (size_type j = 0; j < spaces_per_tab; j++)
 				out_str += SPACE;
 		}
 		else
 		{
-			out_str += string[i];
+			out_str += str[i];
 		}
 	}
-	string = out_str;
+	str = out_str;
 }
 
 //static
diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
index 124f22d5cfc9f69eb66c55f83f8e89bde5b89de6..2e3db2308f075e622bb932f9f5583331bc849126 100644
--- a/indra/llinventory/llinventory.cpp
+++ b/indra/llinventory/llinventory.cpp
@@ -13,7 +13,7 @@
 #include "llinventory.h"
 
 #include "lldbstrings.h"
-#include "llcrypto.h"
+#include "llxorcipher.h"
 #include "llsd.h"
 #include "message.h"
 #include <boost/tokenizer.hpp>
diff --git a/indra/llmessage/llblowfishcipher.cpp b/indra/llmessage/llblowfishcipher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0255a654df882188b8113110300f2ec314651696
--- /dev/null
+++ b/indra/llmessage/llblowfishcipher.cpp
@@ -0,0 +1,135 @@
+/** 
+ * @file llblowcipher.cpp
+ * @brief Wrapper around OpenSSL Blowfish encryption algorithm.
+ *
+ * We do not have OpenSSL headers or libraries on Windows, so this
+ * class only works on Linux.
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llblowfishcipher.h"
+
+
+LLBlowfishCipher::LLBlowfishCipher(const U8* secret, size_t secret_size)
+:	LLCipher()
+{
+	llassert(secret);
+
+	mSecretSize = secret_size;
+	mSecret = new U8[mSecretSize];
+	memcpy(mSecret, secret, mSecretSize);
+}
+
+LLBlowfishCipher::~LLBlowfishCipher()
+{
+	delete [] mSecret;
+	mSecret = NULL;
+}
+
+
+#if LL_LINUX
+
+#include <openssl/evp.h>
+
+// virtual
+U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+	if (!src || !src_len || !dst || !dst_len) return 0;
+	if (src_len > dst_len) return 0;
+
+	// OpenSSL uses "cipher contexts" to hold encryption parameters.
+    EVP_CIPHER_CTX context;
+    EVP_CIPHER_CTX_init(&context);
+
+	// We want a blowfish cyclic block chain cipher, but need to set 
+	// the key length before we pass in a key, so call EncryptInit 
+	// first with NULLs.
+	EVP_EncryptInit_ex(&context, EVP_bf_cbc(), NULL, NULL, NULL);
+	EVP_CIPHER_CTX_set_key_length(&context, (int)mSecretSize);
+	
+	// Complete initialization.  Per EVP_EncryptInit man page, the
+	// cipher pointer must be NULL.  Apparently initial_vector must
+	// be 8 bytes for blowfish, as this is the block size.
+    unsigned char initial_vector[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+	EVP_EncryptInit_ex(&context, NULL, NULL, mSecret, initial_vector);
+
+    int blocksize = EVP_CIPHER_CTX_block_size(&context);
+    int keylen = EVP_CIPHER_CTX_key_length(&context);
+    int iv_length = EVP_CIPHER_CTX_iv_length(&context);
+    lldebugs << "LLBlowfishCipher blocksize " << blocksize
+		<< " keylen " << keylen
+		<< " iv_len " << iv_length
+		<< llendl;
+
+    int output_len = 0;
+    if (!EVP_EncryptUpdate(&context,
+                dst,
+                &output_len,
+                src,
+                src_len))
+    {
+        llwarns << "LLBlowfishCipher::encrypt EVP_EncryptUpdate failure" << llendl;
+        return 0;
+    }
+
+	// There may be some final data left to encrypt if the input is
+	// not an exact multiple of the block size.
+    int temp_len = 0;
+    if (!EVP_EncryptFinal_ex(&context, (unsigned char*)(dst + output_len), &temp_len))
+    {
+        llwarns << "LLBlowfishCipher::encrypt EVP_EncryptFinal failure" << llendl;
+        return 0;
+    }
+    output_len += temp_len;
+	return output_len;
+}
+
+// virtual
+U32 LLBlowfishCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+	llerrs << "LLBlowfishCipher decrypt unsupported" << llendl;
+	return 0;
+}
+
+// virtual
+U32 LLBlowfishCipher::requiredEncryptionSpace(U32 len) const
+{
+	// *HACK: We know blowfish uses an 8 byte block size.
+	// Oddly, sometimes EVP_Encrypt produces an extra block
+	// if the input is an exact multiple of the block size.
+	// So round up.
+	const U32 BLOCK_SIZE = 8;
+	len += BLOCK_SIZE;
+	len -= (len % BLOCK_SIZE);
+	return len;
+}
+
+#else	// !LL_LINUX
+
+// virtual
+U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+	llerrs << "LLBlowfishCipher only supported on Linux" << llendl;
+	return 0;
+}
+
+// virtual
+U32 LLBlowfishCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+	llerrs << "LLBlowfishCipher only supported on Linux" << llendl;
+	return 0;
+}
+
+// virtual
+U32 LLBlowfishCipher::requiredEncryptionSpace(U32 len) const
+{
+	llerrs << "LLBlowfishCipher only supported on Linux" << llendl;
+	return 0;
+}
+
+#endif
+
diff --git a/indra/llmessage/llblowfishcipher.h b/indra/llmessage/llblowfishcipher.h
new file mode 100644
index 0000000000000000000000000000000000000000..3c5ee1a626df9d2ce3186c7c76c46ba5c97bec64
--- /dev/null
+++ b/indra/llmessage/llblowfishcipher.h
@@ -0,0 +1,39 @@
+/** 
+ * @file llblowfishcipher.h
+ * @brief A symmetric block cipher, designed in 1993 by Bruce Schneier.
+ * We use it because it has an 8 byte block size, allowing encryption of
+ * two UUIDs and a timestamp (16x2 + 4 = 36 bytes) with only 40 bytes of
+ * output.  AES has a block size of 32 bytes, so this would require 64 bytes.
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLBLOWFISHCIPHER_H
+#define LLBLOWFISHCIPHER_H
+
+#include "llcipher.h"
+
+
+class LLBlowfishCipher : public LLCipher
+{
+public:
+	// Secret may be up to 56 bytes in length per Blowfish spec.
+	LLBlowfishCipher(const U8* secret, size_t secret_size);
+	virtual ~LLBlowfishCipher();
+
+	// See llcipher.h for documentation.
+	/*virtual*/ U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
+	/*virtual*/ U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
+	/*virtual*/ U32 requiredEncryptionSpace(U32 src_len) const;
+
+#ifdef _DEBUG
+	static BOOL testHarness();
+#endif
+
+private:
+	U8* mSecret;
+	size_t mSecretSize;
+};
+
+#endif // LL_LLCRYPTO_H
diff --git a/indra/llmessage/llcipher.h b/indra/llmessage/llcipher.h
new file mode 100644
index 0000000000000000000000000000000000000000..3dce949c2e0b576bbc43d14a2784a30a992b83a9
--- /dev/null
+++ b/indra/llmessage/llcipher.h
@@ -0,0 +1,38 @@
+/** 
+ * @file llcipher.h
+ * @brief Abstract base class for encryption ciphers.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLCIPHER_H
+#define LLCIPHER_H
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLCipher
+//
+// Abstract base class for a cipher object.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLCipher
+{
+public:
+	virtual ~LLCipher() {}
+
+	// encrypt src and place result into dst. returns TRUE if
+	// Returns number of bytes written into dst, or 0 on error.
+	virtual U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0;
+
+	// decrypt src and place result into dst. 
+	// Returns number of bytes written into dst, or 0 on error.
+	virtual U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0;
+
+	// returns the minimum amount of space required to encrypt for a 
+	// unencrypted source buffer of length len.
+	// *NOTE: This is estimated space and you should check the return value
+	// of the encrypt function.
+	virtual U32 requiredEncryptionSpace(U32 src_len) const = 0 ;
+};
+
+#endif
diff --git a/indra/llmessage/llfiltersd2xmlrpc.cpp b/indra/llmessage/llfiltersd2xmlrpc.cpp
index 6d5f92e98305e406b50811ce9b1255d964ee8a3a..5f814713488fb514d92c558b1230d6eed80ee328 100644
--- a/indra/llmessage/llfiltersd2xmlrpc.cpp
+++ b/indra/llmessage/llfiltersd2xmlrpc.cpp
@@ -248,6 +248,7 @@ void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
 		LLSD::Binary buffer = sd.asBinary();
 		if(!buffer.empty())
 		{
+			// *TODO: convert to LLBase64
 			int b64_buffer_length = apr_base64_encode_len(buffer.size());
 			char* b64_buffer = new char[b64_buffer_length];
 			b64_buffer_length = apr_base64_encode_binary(
diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp
index 4dd550901e6a428dea089dcb4146478c17f06217..54179760064fe72c02c11d0bc16aefe142518d0a 100644
--- a/indra/llmessage/llmail.cpp
+++ b/indra/llmessage/llmail.cpp
@@ -8,6 +8,8 @@
 
 #include "linden_common.h"
 
+#include "llmail.h"
+
 // APR on Windows needs full windows headers
 #ifdef LL_WINDOWS
 #	undef WIN32_LEAN_AND_MEAN
@@ -19,14 +21,16 @@
 #include <sstream>
 #include <boost/regex.hpp>
 
-#include "llmail.h"
-
 #include "apr-1/apr_pools.h"
 #include "apr-1/apr_network_io.h"
 
 #include "llapr.h"
+#include "llbase64.h"	// IM-to-email address
+#include "llblowfishcipher.h"
 #include "llerror.h"
 #include "llhost.h"
+#include "llstring.h"
+#include "lluuid.h"
 #include "net.h"
 
 //
@@ -86,11 +90,12 @@ void disconnect_smtp()
 
 // Returns TRUE on success.
 // message should NOT be SMTP escaped.
-BOOL send_mail(const char* from_name, const char* from_address,
+// static
+BOOL LLMail::send(const char* from_name, const char* from_address,
 			   const char* to_name, const char* to_address,
 			   const char* subject, const char* message)
 {
-	std::string header = build_smtp_transaction(
+	std::string header = buildSMTPTransaction(
 		from_name,
 		from_address,
 		to_name,
@@ -106,12 +111,13 @@ BOOL send_mail(const char* from_name, const char* from_address,
 	{
 		message_str = message;
 	}
-	bool rv = send_mail(header, message_str, to_address, from_address);
+	bool rv = send(header, message_str, to_address, from_address);
 	if(rv) return TRUE;
 	return FALSE;
 }
 
-void init_mail(const std::string& hostname, apr_pool_t* pool)
+// static
+void LLMail::init(const std::string& hostname, apr_pool_t* pool)
 {
 	gMailSocket = NULL;
 	if(hostname.empty() || !pool)
@@ -138,12 +144,14 @@ void init_mail(const std::string& hostname, apr_pool_t* pool)
 	}
 }
 
-void enable_mail(bool mail_enabled)
+// static
+void LLMail::enable(bool mail_enabled)
 {
 	gMailEnabled = mail_enabled;
 }
 
-std::string build_smtp_transaction(
+// static
+std::string LLMail::buildSMTPTransaction(
 	const char* from_name,
 	const char* from_address,
 	const char* to_name,
@@ -197,7 +205,8 @@ std::string build_smtp_transaction(
 	return header.str();
 }
 
-bool send_mail(
+// static
+bool LLMail::send(
 	const std::string& header,
 	const std::string& message,
 	const char* from_address,
@@ -290,3 +299,56 @@ bool send_mail(
 #endif
 	return true;
 }
+
+
+// static
+std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id,
+											const LLUUID& to_agent_id,
+											U32 time,
+											const U8* secret,
+											size_t secret_size)
+{
+	size_t data_size = 4 + UUID_BYTES + UUID_BYTES;
+	// Convert input data into a binary blob
+	std::vector<U8> data;
+	data.resize(data_size);
+	// *NOTE: This may suffer from endian issues.  Could be htonmemcpy.
+	memcpy(&data[0], &time, 4);
+	memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES);
+	memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES);
+	
+	// Encrypt the blob
+	LLBlowfishCipher cipher(secret, secret_size);
+	size_t encrypted_size = cipher.requiredEncryptionSpace(data.size());
+	U8* encrypted = new U8[encrypted_size];
+	cipher.encrypt(&data[0], data_size, encrypted, encrypted_size);
+
+	// Base64 encoded and replace the pieces of base64 that are less compatible 
+	// with e-mail local-parts.
+	// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
+	std::string address = LLBase64::encode(encrypted, encrypted_size);
+	LLString::replaceChar(address, '+', '-');
+	LLString::replaceChar(address, '/', '_');
+
+	// Strip padding = signs, see RFC
+	size_t extra_bytes = encrypted_size % 3;
+	size_t padding_size = 0;
+	if (extra_bytes == 0)
+	{
+		padding_size = 0;
+	}
+	else if (extra_bytes == 1)
+	{
+		padding_size = 2;
+	}
+	else if (extra_bytes == 2)
+	{
+		padding_size = 1;
+	}
+
+	address.resize(address.size() - padding_size);
+
+	delete [] encrypted;
+
+	return address;
+}
diff --git a/indra/llmessage/llmail.h b/indra/llmessage/llmail.h
index af0226477653cca762787c1c7c6ba2e4ed0ec007..dc642b96d500a4a794053000f681b1b59a9ccea1 100644
--- a/indra/llmessage/llmail.h
+++ b/indra/llmessage/llmail.h
@@ -11,55 +11,84 @@
 
 typedef struct apr_pool_t apr_pool_t;
 
-// if hostname is NULL, then the host is resolved as 'mail'
-void init_mail(const std::string& hostname, apr_pool_t* pool);
+class LLUUID;
 
-// Allow all email transmission to be disabled/enabled.
-void enable_mail(bool mail_enabled);
+class LLMail
+{
+public:
+	// if hostname is NULL, then the host is resolved as 'mail'
+	static void init(const std::string& hostname, apr_pool_t* pool);
 
-// returns TRUE if the call succeeds, FALSE otherwise.
-//
-// Results in:
-// From: "from_name" <from_address>
-// To:   "to_name" <to_address>
-// Subject: subject
-// message
-BOOL send_mail(const char* from_name, const char* from_address,
-			   const char* to_name, const char* to_address,
-			   const char* subject, const char* message);
+	// Allow all email transmission to be disabled/enabled.
+	static void enable(bool mail_enabled);
 
-/**
- * @brief build the complete smtp transaction & header for use in an
- * mail.
- *
- * @param from_name The name of the email sender
- * @param from_address The email address for the sender
- * @param to_name The name of the email recipient
- * @param to_name The email recipient address
- * @param subject The subject of the email
- * @return Returns the complete SMTP transaction mail header.
- */
-std::string build_smtp_transaction(
-	const char* from_name,
-	const char* from_address,
-	const char* to_name,
-	const char* to_address,
-	const char* subject);
+	// returns TRUE if the call succeeds, FALSE otherwise.
+	//
+	// Results in:
+	// From: "from_name" <from_address>
+	// To:   "to_name" <to_address>
+	// Subject: subject
+	// message
+	static BOOL send(const char* from_name, const char* from_address,
+				const char* to_name, const char* to_address,
+				const char* subject, const char* message);
 
-/**
- * @brief send an email with header and body.
- *
- * @param header The email header. Use build_mail_header().
- * @param message The unescaped email message.
- * @param from_address Used for debugging
- * @param to_address Used for debugging
- * @return Returns true if the message could be sent.
- */
-bool send_mail(
-	const std::string& header,
-	const std::string& message,
-	const char* from_address,
-	const char* to_address);
+	/**
+	* @brief build the complete smtp transaction & header for use in an
+	* mail.
+	*
+	* @param from_name The name of the email sender
+	* @param from_address The email address for the sender
+	* @param to_name The name of the email recipient
+	* @param to_name The email recipient address
+	* @param subject The subject of the email
+	* @return Returns the complete SMTP transaction mail header.
+	*/
+	static std::string buildSMTPTransaction(
+		const char* from_name,
+		const char* from_address,
+		const char* to_name,
+		const char* to_address,
+		const char* subject);
+
+	/**
+	* @brief send an email with header and body.
+	*
+	* @param header The email header. Use build_mail_header().
+	* @param message The unescaped email message.
+	* @param from_address Used for debugging
+	* @param to_address Used for debugging
+	* @return Returns true if the message could be sent.
+	*/
+	static bool send(
+		const std::string& header,
+		const std::string& message,
+		const char* from_address,
+		const char* to_address);
+
+	// IM-to-email sessions use a "session id" based on an encrypted
+	// combination of from agent_id, to agent_id, and timestamp.  When
+	// a user replies to an email we use the from_id to determine the
+	// sender's name and the to_id to route the message.  The address
+	// is encrypted to prevent users from building addresses to spoof
+	// IMs from other users.  The timestamps allow the "sessions" to 
+	// expire, in case one of the sessions is stolen/hijacked.
+	//
+	// indra/tools/mailglue is responsible for parsing the inbound mail.
+	//
+	// secret: binary blob passed to blowfish, max length 56 bytes
+	// secret_size: length of blob, in bytes
+	//
+	// Returns: "base64" encoded email local-part, with _ and - as the
+	// non-alphanumeric characters.  This allows better compatibility
+	// with email systems than the default / and + extra chars.  JC
+	static std::string encryptIMEmailAddress(
+		const LLUUID& from_agent_id,
+		const LLUUID& to_agent_id,
+		U32 time,
+		const U8* secret,
+		size_t secret_size);
+};
 
 extern const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE;
 
diff --git a/indra/llmessage/llnullcipher.cpp b/indra/llmessage/llnullcipher.cpp
index 53bb7484151911b598aadc6f3759ad060dcd9ba7..0d28b5c3938560cc16fbe6d6cabc4ccc156b8182 100644
--- a/indra/llmessage/llnullcipher.cpp
+++ b/indra/llmessage/llnullcipher.cpp
@@ -8,33 +8,33 @@
 
 #include "linden_common.h"
 
-#include "llcrypto.h"
+#include "llnullcipher.h"
 
 ///----------------------------------------------------------------------------
 /// Class LLNullCipher
 ///----------------------------------------------------------------------------
 
-BOOL LLNullCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+U32  LLNullCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
 {
 	if((src_len == dst_len) && src && dst)
 	{
 		memmove(dst, src, src_len);
-		return TRUE;
+		return src_len;
 	}
-	return FALSE;
+	return 0;
 }
 
-BOOL LLNullCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+U32 LLNullCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
 {
 	if((src_len == dst_len) && src && dst)
 	{
 		memmove(dst, src, src_len);
-		return TRUE;
+		return src_len;
 	}
-	return FALSE;
+	return 0;
 }
 
-U32 LLNullCipher::requiredEncryptionSpace(U32 len)
+U32 LLNullCipher::requiredEncryptionSpace(U32 len) const
 {
 	return len;
 }
diff --git a/indra/llmessage/llnullcipher.h b/indra/llmessage/llnullcipher.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc8c2f2fa3a0db3724980670242619b252dcaa04
--- /dev/null
+++ b/indra/llmessage/llnullcipher.h
@@ -0,0 +1,30 @@
+/** 
+ * @file llnullcipher.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLNULLCIPHER_H
+#define LLNULLCIPHER_H
+
+#include "llcipher.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLNullCipher
+//
+// A class which implements LLCipher, but does not transform src
+// during encryption.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLNullCipher : public LLCipher
+{
+public:
+	LLNullCipher() {}
+	virtual ~LLNullCipher() {}
+	virtual U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
+	virtual U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
+	virtual U32 requiredEncryptionSpace(U32 src_len) const;
+};
+
+#endif
diff --git a/indra/llmessage/llxorcipher.cpp b/indra/llmessage/llxorcipher.cpp
index 1fbbfec9e079525a9f0d985c1e519d286a2276fd..9ddcf091b2d53c07441b7b7c85f8e05187152a3b 100644
--- a/indra/llmessage/llxorcipher.cpp
+++ b/indra/llmessage/llxorcipher.cpp
@@ -8,7 +8,8 @@
 
 #include "linden_common.h"
 
-#include "llcrypto.h"
+#include "llxorcipher.h"
+
 #include "llerror.h"
 
 ///----------------------------------------------------------------------------
@@ -44,25 +45,26 @@ LLXORCipher& LLXORCipher::operator=(const LLXORCipher& cipher)
 	return *this;
 }
 
-BOOL LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+U32 LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
 {
-	if(!src || !src_len || !dst || !dst_len || !mPad) return FALSE;
+	if(!src || !src_len || !dst || !dst_len || !mPad) return 0;
 	U8* pad_end = mPad + mPadLen;
-	while(src_len--)
+	U32 count = src_len;
+	while(count--)
 	{
 		*dst++ = *src++ ^ *mHead++;
 		if(mHead >= pad_end) mHead = mPad;
 	}
-	return TRUE;
+	return src_len;
 }
 
-BOOL LLXORCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+U32 LLXORCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
 {
 	// xor is a symetric cipher, thus, just call the other function.
 	return encrypt(src, src_len, dst, dst_len);
 }
 
-U32 LLXORCipher::requiredEncryptionSpace(U32 len)
+U32 LLXORCipher::requiredEncryptionSpace(U32 len) const
 {
 	return len;
 }
diff --git a/indra/llmessage/llxorcipher.h b/indra/llmessage/llxorcipher.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f0f58f62fff94996c0aacaa322006e9e9e24373
--- /dev/null
+++ b/indra/llmessage/llxorcipher.h
@@ -0,0 +1,49 @@
+/** 
+ * @file llxorcipher.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLXORCIPHER_H
+#define LLXORCIPHER_H
+
+#include "llcipher.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLXORCipher
+//
+// Implementation of LLCipher which encrypts using a XOR pad.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLXORCipher : public LLCipher
+{
+public:
+	LLXORCipher(const U8* pad, U32 pad_len);
+	LLXORCipher(const LLXORCipher& cipher);
+	virtual ~LLXORCipher();
+	LLXORCipher& operator=(const LLXORCipher& cipher);
+
+	// Cipher functions
+	/*virtual*/ U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
+	/*virtual*/ U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len);
+	/*virtual*/ U32 requiredEncryptionSpace(U32 src_len) const;
+
+	// special syntactic-sugar since xor can be performed in place.
+	BOOL encrypt(U8* buf, U32 len) { return encrypt((const U8*)buf, len, buf, len); }
+	BOOL decrypt(U8* buf, U32 len) { return decrypt((const U8*)buf, len, buf, len); }
+
+#ifdef _DEBUG
+	// This function runs tests to make sure the crc is
+	// working. Returns TRUE if it is.
+	static BOOL testHarness();
+#endif
+
+protected:
+	void init(const U8* pad, U32 pad_len);
+	U8* mPad;
+	U8* mHead;
+	U32 mPadLen;
+};
+
+#endif
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 4ff07e997645a54c15c4845781ff9b76369de9af..7bd6c9ad82170c5a31460445c062e985e1fd7212 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -25,7 +25,6 @@
 #include "audiosettings.h"
 #include "llcachename.h"
 #include "llviewercontrol.h"
-#include "llcrypto.h"
 #include "lldir.h"
 #include "lleconomy.h"
 #include "llerrorcontrol.h"
@@ -46,6 +45,7 @@
 #include "llversion.h"
 #include "llvfs.h"
 #include "llwindow.h"		// for shell_open
+#include "llxorcipher.h"	// saved password, MAC address
 #include "message.h"
 #include "v3math.h"
 
diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h
index 50b84babd130b66d33bb849390b82c1639dc61a6..e08c040855b9faed77893dcce46c2da787a3fa09 100644
--- a/indra/newview/llviewerprecompiledheaders.h
+++ b/indra/newview/llviewerprecompiledheaders.h
@@ -151,7 +151,6 @@
 #include "llcachename.h"
 #include "llcallbacklisth.h"
 #include "llcircuit.h"
-#include "llcrypto.h"
 #include "lldatapacker.h"
 #include "lldbstrings.h"
 #include "lldispatcher.h"
diff --git a/indra/test/blowfish.1.bin b/indra/test/blowfish.1.bin
new file mode 100644
index 0000000000000000000000000000000000000000..61286e45e3ab5b8d1a91927d58ebc870c88c3fb8
--- /dev/null
+++ b/indra/test/blowfish.1.bin
@@ -0,0 +1 @@
+.A„Ä3ŒLÜE ``òøÝKÛ@¼ûÇ;M[ÚBë·ø„>ËÊC—'
\ No newline at end of file
diff --git a/indra/test/blowfish.2.bin b/indra/test/blowfish.2.bin
new file mode 100644
index 0000000000000000000000000000000000000000..ef72d96bbfd83b80f37b35b6522591174ff921a9
Binary files /dev/null and b/indra/test/blowfish.2.bin differ
diff --git a/indra/test/blowfish.digits.txt b/indra/test/blowfish.digits.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fce1fed943694cdec72680e515907800235a8cc1
--- /dev/null
+++ b/indra/test/blowfish.digits.txt
@@ -0,0 +1 @@
+01234567890123456789012345678901234
diff --git a/indra/test/blowfish.pl b/indra/test/blowfish.pl
new file mode 100755
index 0000000000000000000000000000000000000000..7940d87aa605b1fd679ae40525283cbaf708aad2
--- /dev/null
+++ b/indra/test/blowfish.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+# *TODO: specify test count here
+use Test::More qw(no_plan);
+
+use Crypt::CBC;
+use MIME::Base64;
+
+my $init_vector = "\x00" x 8;
+# my $key = pack("H*", "ef5a8376eb0c99fe0dafa487d15bec19cae63d1e25fe31d8d92f7ab0398246d70ee733108e47360e16359654571cf5bab6c3375b42cee4fa");
+# my $key = "d263eb8a78034e40";
+         #"8d082918aa369174";
+my $key = "\x00" x 16;
+
+my $cipher = Crypt::CBC->new( { cipher => 'Blowfish',
+								regenerate_key => 0,
+							  	key => $key,
+							  	iv => $init_vector,
+							  	header => 'none',
+								prepend_iv => 0,
+							  	keysize => 16 } );
+
+#my $blocks = $cipher->blocksize();
+#print "blocksize $blocks\n";
+
+my $len;
+my $input = "01234567890123456789012345678901234\n";
+#my $input = "a whale of a tale I tell you lad, a whale of a tale for me, a whale of a tale and the fiddlers three";
+$len = length($input);
+is ($len, 36, "input length");
+
+$len = length($key);
+is ($len, 16, "key length");
+
+
+my $encrypted = $cipher->encrypt($input);
+is (length($encrypted), 40, "encrypted length");
+
+open(FH, "blowfish.1.bin");
+my $bin = scalar <FH>;
+is ($encrypted, $bin, "matches openssl");
+close(FH);
+
+my $base64 = encode_base64($encrypted);
+is ($base64, "LkGExDOMTNxFIGBg8gP43UvbQLz7xztNWwYF2kLrtwT4hD7LykOXJw==\n",
+	"base64 output");
+
+my $unbase64 = decode_base64($base64);
+is( $encrypted, $unbase64, "reverse base64" );
+
+my $output = $cipher->decrypt($unbase64);
+is ($input, $output, "reverse encrypt");
+
+$key = pack("H[32]", "526a1e07a19dbaed84c4ff08a488d15e");
+$cipher = Crypt::CBC->new( { cipher => 'Blowfish',
+								regenerate_key => 0,
+							  	key => $key,
+							  	iv => $init_vector,
+							  	header => 'none',
+								prepend_iv => 0,
+							  	keysize => 16 } );
+$encrypted = $cipher->encrypt($input);
+is (length($encrypted), 40, "uuid encrypted length");
+$output = $cipher->decrypt($encrypted);
+is ($input, $output, "uuid reverse encrypt");
+
+open(FH, "blowfish.2.bin");
+$bin = scalar <FH>;
+close(FH);
+is( $encrypted, $bin, "uuid matches openssl" );
+
+print encode_base64($encrypted);
diff --git a/indra/test/llblowfish_tut.cpp b/indra/test/llblowfish_tut.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f0912d98de6901e4a52440efcd0a06a5c801090
--- /dev/null
+++ b/indra/test/llblowfish_tut.cpp
@@ -0,0 +1,125 @@
+/** 
+ * @file llblowfish_tut.cpp
+ * @author James Cook, james@lindenlab.com
+ * @date 2007-02-04
+ *
+ * Data files generated with:
+ * openssl enc -bf-cbc -in blowfish.digits.txt -out blowfish.1.bin -K 00000000000000000000000000000000 -iv 0000000000000000 -p
+ * openssl enc -bf-cbc -in blowfish.digits.txt -out blowfish.2.bin -K 526a1e07a19dbaed84c4ff08a488d15e -iv 0000000000000000 -p
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+
+#include "llblowfishcipher.h"
+
+#include <string>
+#include <stdio.h>
+#include "lluuid.h"
+
+namespace tut
+{
+	class LLData
+	{
+	public:
+		unsigned char* mInput;
+		int mInputSize;
+
+		LLData()
+		{
+			// \n to make it easier to create text files
+			// for testing with command line openssl
+			mInput = (unsigned char*)"01234567890123456789012345678901234\n";
+			mInputSize = 36;
+		}
+
+		bool matchFile(const char* filename,
+				const std::string& data)
+		{
+			FILE* fp = fopen(filename, "rb");
+			if (!fp) 
+			{
+				// sometimes test is run inside the indra directory
+				std::string path = "test/";
+				path += filename;
+				fp = fopen(path.c_str(), "rb");
+			}
+			if (!fp)
+			{
+				llwarns << "unabled to open " << filename << llendl;
+				return false;
+			}
+
+			std::string good;
+			good.resize(256);
+			size_t got = fread(&good[0], 1, 256, fp);
+			lldebugs << "matchFile read " << got << llendl;
+			fclose(fp);
+			good.resize(got);
+		
+			return (good == data);
+		}
+	};
+	typedef test_group<LLData> blowfish_test;
+	typedef blowfish_test::object blowfish_object;
+	// Create test with name that can be selected on
+	// command line of test app.
+	tut::blowfish_test blowfish("blowfish");
+
+	template<> template<>
+	void blowfish_object::test<1>()
+	{
+		LLUUID blank;
+		LLBlowfishCipher cipher(&blank.mData[0], UUID_BYTES);
+
+		U32 dst_len = cipher.requiredEncryptionSpace(36);
+		ensure("encryption space 36",
+				(dst_len == 40) );
+
+		// Blowfish adds an additional 8-byte block if your
+		// input is an exact multiple of 8
+		dst_len = cipher.requiredEncryptionSpace(8);
+		ensure("encryption space 8",
+				(dst_len == 16)  );
+	}
+
+	template<> template<>
+	void blowfish_object::test<2>()
+	{
+		LLUUID blank;
+		LLBlowfishCipher cipher(&blank.mData[0], UUID_BYTES);
+
+		std::string result;
+		result.resize(256);
+		U32 count = cipher.encrypt(mInput, mInputSize,
+				(U8*) &result[0], 256);
+
+		ensure("encrypt output count",
+				(count == 40) );
+		result.resize(count);
+
+		ensure("encrypt null key", matchFile("blowfish.1.bin", result));
+	}
+
+	template<> template<>
+	void blowfish_object::test<3>()
+	{
+		// same as base64 test id
+		LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e");
+		LLBlowfishCipher cipher(&id.mData[0], UUID_BYTES);
+
+		std::string result;
+		result.resize(256);
+		U32 count = cipher.encrypt(mInput, mInputSize,
+				(U8*) &result[0], 256);
+
+		ensure("encrypt output count",
+				(count == 40) );
+		result.resize(count);
+
+		ensure("encrypt real key", matchFile("blowfish.2.bin", result));
+	}
+}