diff options
| author | James Cook <james@lindenlab.com> | 2007-02-15 20:24:08 +0000 | 
|---|---|---|
| committer | James Cook <james@lindenlab.com> | 2007-02-15 20:24:08 +0000 | 
| commit | 12ac04231b8d358e70c830f7958f7efbc0f7c0d1 (patch) | |
| tree | 189b9bc4e3bfa63275cc41be8dc42a28b0f54ffe | |
| parent | 4feabe4a9caec380cad405d5410bc762c862113d (diff) | |
merge -r 57761:57967 im-email-session-3, removes database load from IM to email session tracking, introduces new format for email return addresses
| -rw-r--r-- | indra/llcommon/llbase64.cpp | 43 | ||||
| -rw-r--r-- | indra/llcommon/llbase64.h | 19 | ||||
| -rw-r--r-- | indra/llcommon/llsdserialize_xml.cpp | 1 | ||||
| -rw-r--r-- | indra/llcommon/llstring.h | 10 | ||||
| -rw-r--r-- | indra/llinventory/llinventory.cpp | 2 | ||||
| -rw-r--r-- | indra/llmessage/llblowfishcipher.cpp | 135 | ||||
| -rw-r--r-- | indra/llmessage/llblowfishcipher.h | 39 | ||||
| -rw-r--r-- | indra/llmessage/llcipher.h | 38 | ||||
| -rw-r--r-- | indra/llmessage/llfiltersd2xmlrpc.cpp | 1 | ||||
| -rw-r--r-- | indra/llmessage/llmail.cpp | 80 | ||||
| -rw-r--r-- | indra/llmessage/llmail.h | 119 | ||||
| -rw-r--r-- | indra/llmessage/llnullcipher.cpp | 16 | ||||
| -rw-r--r-- | indra/llmessage/llnullcipher.h | 30 | ||||
| -rw-r--r-- | indra/llmessage/llxorcipher.cpp | 16 | ||||
| -rw-r--r-- | indra/llmessage/llxorcipher.h | 49 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewerprecompiledheaders.h | 1 | ||||
| -rw-r--r-- | indra/test/blowfish.1.bin | 1 | ||||
| -rw-r--r-- | indra/test/blowfish.2.bin | bin | 0 -> 40 bytes | |||
| -rw-r--r-- | indra/test/blowfish.digits.txt | 1 | ||||
| -rwxr-xr-x | indra/test/blowfish.pl | 74 | ||||
| -rw-r--r-- | indra/test/llblowfish_tut.cpp | 125 | 
22 files changed, 725 insertions, 77 deletions
| diff --git a/indra/llcommon/llbase64.cpp b/indra/llcommon/llbase64.cpp new file mode 100644 index 0000000000..88e9d960a2 --- /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 0000000000..6e8b817ba7 --- /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 c21b8f19a4..796cd9f9f4 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 a381af74d0..6e9ea0b5a0 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 124f22d5cf..2e3db2308f 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 0000000000..0255a654df --- /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 0000000000..3c5ee1a626 --- /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 0000000000..3dce949c2e --- /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 6d5f92e983..5f81471348 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 4dd550901e..5417976006 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 af02264776..dc642b96d5 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 53bb748415..0d28b5c393 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 0000000000..bc8c2f2fa3 --- /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 1fbbfec9e0..9ddcf091b2 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 0000000000..1f0f58f62f --- /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 4ff07e9976..7bd6c9ad82 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 50b84babd1..e08c040855 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 0000000000..61286e45e3 --- /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.binBinary files differ new file mode 100644 index 0000000000..ef72d96bbf --- /dev/null +++ b/indra/test/blowfish.2.bin diff --git a/indra/test/blowfish.digits.txt b/indra/test/blowfish.digits.txt new file mode 100644 index 0000000000..fce1fed943 --- /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 0000000000..7940d87aa6 --- /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 0000000000..8f0912d98d --- /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)); +	} +} | 
