From 12ac04231b8d358e70c830f7958f7efbc0f7c0d1 Mon Sep 17 00:00:00 2001 From: James Cook Date: Thu, 15 Feb 2007 20:24:08 +0000 Subject: merge -r 57761:57967 im-email-session-3, removes database load from IM to email session tracking, introduces new format for email return addresses --- indra/llcommon/llbase64.cpp | 43 +++++++++ indra/llcommon/llbase64.h | 19 ++++ indra/llcommon/llsdserialize_xml.cpp | 1 + indra/llcommon/llstring.h | 10 +-- indra/llinventory/llinventory.cpp | 2 +- indra/llmessage/llblowfishcipher.cpp | 135 +++++++++++++++++++++++++++++ indra/llmessage/llblowfishcipher.h | 39 +++++++++ indra/llmessage/llcipher.h | 38 ++++++++ indra/llmessage/llfiltersd2xmlrpc.cpp | 1 + indra/llmessage/llmail.cpp | 80 +++++++++++++++-- indra/llmessage/llmail.h | 119 +++++++++++++++---------- indra/llmessage/llnullcipher.cpp | 16 ++-- indra/llmessage/llnullcipher.h | 30 +++++++ indra/llmessage/llxorcipher.cpp | 16 ++-- indra/llmessage/llxorcipher.h | 49 +++++++++++ indra/newview/llstartup.cpp | 2 +- indra/newview/llviewerprecompiledheaders.h | 1 - indra/test/blowfish.1.bin | 1 + indra/test/blowfish.2.bin | Bin 0 -> 40 bytes indra/test/blowfish.digits.txt | 1 + indra/test/blowfish.pl | 74 ++++++++++++++++ indra/test/llblowfish_tut.cpp | 125 ++++++++++++++++++++++++++ 22 files changed, 725 insertions(+), 77 deletions(-) create mode 100644 indra/llcommon/llbase64.cpp create mode 100644 indra/llcommon/llbase64.h create mode 100644 indra/llmessage/llblowfishcipher.cpp create mode 100644 indra/llmessage/llblowfishcipher.h create mode 100644 indra/llmessage/llcipher.h create mode 100644 indra/llmessage/llnullcipher.h create mode 100644 indra/llmessage/llxorcipher.h create mode 100644 indra/test/blowfish.1.bin create mode 100644 indra/test/blowfish.2.bin create mode 100644 indra/test/blowfish.digits.txt create mode 100755 indra/test/blowfish.pl create mode 100644 indra/test/llblowfish_tut.cpp (limited to 'indra') 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 + +#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 << ""; 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::replaceNonstandardASCII( std::basic_string& string, T r //static template -void LLStringBase::replaceTabsWithSpaces( std::basic_string& string, size_type spaces_per_tab ) +void LLStringBase::replaceTabsWithSpaces( std::basic_string& str, size_type spaces_per_tab ) { llassert( spaces_per_tab >= 0 ); @@ -913,19 +913,19 @@ void LLStringBase::replaceTabsWithSpaces( std::basic_string& string, size_ LLStringBase 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 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 + +// 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 #include -#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 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" -// To: "to_name" -// 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" + // To: "to_name" + // 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.bin new file mode 100644 index 0000000000..ef72d96bbf 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 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 ; +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 ; +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 +#include +#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 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)); + } +} -- cgit v1.2.3