summaryrefslogtreecommitdiff
path: root/common/crypto.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /common/crypto.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'common/crypto.cpp')
-rw-r--r--common/crypto.cpp2219
1 files changed, 2219 insertions, 0 deletions
diff --git a/common/crypto.cpp b/common/crypto.cpp
new file mode 100644
index 0000000..2805b78
--- /dev/null
+++ b/common/crypto.cpp
@@ -0,0 +1,2219 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+// Note: not using precompiled headers. The crypto++ headers create gunk that anyone including them
+// has to link to, so they can't be included in the global project file. They also contain
+// string functions which are deprecated by the global project file so they can't be included after, either.
+// So we can't use the global precompiled header, need to manually include the things we need.
+
+#include "winlite.h"
+
+#ifdef POSIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+/* this stuff needs to be before the crypto headers, as it relies on memdbg, which doesn't
+ do the right thing in the face of xdebug getting included below */
+// tier0
+//#include "tier0/tier0.h"
+#include "tier0/basetypes.h"
+#include "tier0/vprof.h"
+//#include "constants.h"
+#include "vstdlib/vstdlib.h"
+#include "strtools.h"
+//#include "version.h"
+//#include "globals.h"
+//#include "ivalidate.h"
+#include "tier1/utlvector.h"
+#include "simplebitstring.h"
+#include "tier1/checksum_sha1.h"
+#include "tier0/memdbgon.h"
+#include "tier0/tslist.h"
+#include "tier0/memdbgoff.h"
+
+
+#ifdef ENABLE_OPENSSLCONNECTION
+#define USE_OPENSSL_AES_DECRYPT 1
+#endif
+
+#ifdef USE_OPENSSL_AES_DECRYPT
+// openssl optimized AES routines
+#include "openssl/aes.h"
+#if defined(_M_IX86) || defined (_M_X64) || defined(__i386__) || defined(__x86_64__)
+#include <emmintrin.h>
+#endif
+#endif
+
+// crypto ++
+#include "tier0/valve_off.h"
+#include "../external/crypto++-5.6.3/cryptopushdisablewarnings.h"
+#if _MSC_VER < 1400 // doesn't work with vc8, things below need xdebug
+#define _XDEBUG_ // keep crypto++-5.2 from including xdebug
+// these are defined in xdebug and used in some subsequent headers, define them to be our version
+#define _NEW_CRT new
+#define _DELETE_CRT(_P) delete (_P)
+#define _DELETE_CRT_VEC(_P) delete[] (_P)
+#define _STRING_CRT string
+#endif
+#define CRYPTOPP_DLL
+#undef min
+#undef max
+#undef Verify
+#define VPROF_BUDGETGROUP_ENCRYPTION _T("Encryption")
+#define SPEW_CRYPTO "crypto"
+const int k_cMedBuff = 1024; // medium buffer
+
+#if defined(GNUC)
+#pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#include "../external/crypto++-5.6.3/cryptlib.h"
+#include "../external/crypto++-5.6.3/osrng.h"
+#include "../external/crypto++-5.6.3/crc.h"
+#include "../external/crypto++-5.6.3/modes.h"
+#include "../external/crypto++-5.6.3/files.h"
+#include "../external/crypto++-5.6.3/hex.h"
+#include "../external/crypto++-5.6.3/base64.h"
+#include "../external/crypto++-5.6.3/base32.h"
+#include "../external/crypto++-5.6.3/words.h"
+#include "../external/crypto++-5.6.3/rsa.h"
+#include "../external/crypto++-5.6.3/aes.h"
+#include "../external/crypto++-5.6.3/hmac.h"
+#include "../external/crypto++-5.6.3/zlib.h"
+#include "../external/crypto++-5.6.3/gzip.h"
+#include "../external/crypto++-5.6.3/pwdbased.h"
+using namespace CryptoPP;
+typedef AutoSeededX917RNG<AES> CAutoSeededRNG;
+#include "../external/crypto++-5.6.3/cryptopopdisablewarnings.h"
+
+#if defined(GNUC)
+#pragma GCC diagnostic warning "-Wshadow"
+#endif
+
+#include "tier0/memdbgon.h"
+#include "tier0/valve_on.h"
+
+#include "crypto.h"
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+
+// list of auto-seeded RNG pointers
+// these are very expensive to construct, so it makes sense to cache them
+CTSList<CAutoSeededRNG> g_tslistPAutoSeededRNG;
+
+// to avoid deconstructor order issuses we allow to manually free the list
+void FreeListRNG()
+{
+ g_tslistPAutoSeededRNG.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: thread-safe access to a pool of cryptoPP random number generators
+//-----------------------------------------------------------------------------
+class CPoolAllocatedRNG
+{
+public:
+ CPoolAllocatedRNG()
+ {
+ m_pRNGNode = g_tslistPAutoSeededRNG.Pop();
+ if ( !m_pRNGNode )
+ {
+ m_pRNGNode = new CTSList<CAutoSeededRNG>::Node_t;
+ }
+ }
+
+ ~CPoolAllocatedRNG()
+ {
+ g_tslistPAutoSeededRNG.Push( m_pRNGNode );
+ }
+
+ CAutoSeededRNG &GetRNG()
+ {
+ return m_pRNGNode->elem;
+ }
+
+private:
+ CTSList<CAutoSeededRNG>::Node_t *m_pRNGNode;
+};
+
+// force run this static construction code
+class CGlobalInitConstructor
+{
+public:
+ CGlobalInitConstructor()
+ {
+ // we have to use this function once since the underlying static constructor
+ // is not thread safe. See use of MicrosoftCryptoProvider in Crypto++
+ CAutoSeededRNG rng;
+ rng.GenerateByte();
+ }
+};
+
+volatile static CGlobalInitConstructor s_StaticCryptoConstructor;
+
+//-----------------------------------------------------------------------------
+// Purpose: Encrypts the specified data with the specified key. Uses AES (Rijndael) symmetric
+// encryption. The encrypted data may then be decrypted by calling SymmetricDecrypt
+// with the same key.
+// Input: pubPlaintextData - Data to be encrypted
+// cubPlaintextData - Size of data to be encrypted
+// pIV - Pointer to initialization vector
+// cubIV - Size of initialization vector
+// pubEncryptedData - Pointer to buffer to receive encrypted data
+// pcubEncryptedData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for encrypted data. When the method returns, this will contain
+// the actual size of the encrypted data.
+// pubKey - the key to encrypt the data with
+// cubKey - Size of the key (must be k_nSymmetricKeyLen)
+// Output: true if successful, false if encryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::SymmetricEncryptWithIV( const uint8 *pubPlaintextData, const uint32 cubPlaintextData,
+ const uint8 *pIV, const uint32 cubIV,
+ uint8 *pubEncryptedData, uint32 *pcubEncryptedData,
+ const uint8 *pubKey, const uint32 cubKey )
+{
+ VPROF_BUDGET( "CCrypto::SymmetricEncrypt", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubPlaintextData );
+ Assert( cubPlaintextData );
+ Assert( pubEncryptedData );
+ Assert( pcubEncryptedData );
+ Assert( *pcubEncryptedData );
+ Assert( pubKey );
+ Assert( k_nSymmetricKeyLen == cubKey ); // the only key length supported is k_nSymmetricKeyLen
+
+ bool bRet = false;
+
+ uint32 cubEncryptedData = *pcubEncryptedData; // remember how big the caller's buffer is
+ bool bUseTempBuffer = false;
+ uint8 *pTemp = pubEncryptedData;
+
+
+ //
+ // Crypto++ does not play well with overlapping buffers. If the buffers are
+ // overlapping, then allocate some temp space to use for the encryption.
+ //
+ // It does work fine with _identical_ buffers.
+ //
+ if ( ( pubEncryptedData + cubEncryptedData >= pubPlaintextData ) &&
+ ( pubPlaintextData + cubPlaintextData >= pubEncryptedData ) )
+ {
+ pTemp = new uint8[cubEncryptedData];
+ bUseTempBuffer = true;
+ }
+
+ try // handle any exceptions crypto++ may throw
+ {
+ if ( pTemp != NULL )
+ {
+ AESEncryption aesEncrypt( pubKey, cubKey );
+
+ byte rgubIVEncrypted[k_cMedBuff];
+ Assert( Q_ARRAYSIZE( rgubIVEncrypted ) >= aesEncrypt.BlockSize() );
+ Assert( pIV != NULL && cubIV >= aesEncrypt.BlockSize() );
+
+ ArraySink * pOutputSink = new ArraySink( pTemp, *pcubEncryptedData );
+
+ // encrypt the initial vector with the key
+ aesEncrypt.ProcessBlock( pIV, rgubIVEncrypted );
+
+ // store the encrypted IV in the output - the recipient will need it
+ pOutputSink->Put( rgubIVEncrypted, aesEncrypt.BlockSize() );
+
+ // encrypt the message, given the key & IV
+ CBC_Mode_ExternalCipher::Encryption cipher( aesEncrypt, pIV );
+ // Note: StreamTransformationFilter now owns the pointer to pOutputSink and will
+ // free it when the filter goes out of scope and destructs
+ StreamTransformationFilter filter( cipher, pOutputSink );
+ filter.Put( (byte *) pubPlaintextData, cubPlaintextData );
+ filter.MessageEnd();
+ // return length of encrypted data to caller
+ *pcubEncryptedData = pOutputSink->TotalPutLength();
+ // CryptoPP may leave garbage hanging around in the caller's buffer past the stated output length.
+ // Just to be safe, zero out caller's buffer from end out output to end of max buffer
+ if ( bUseTempBuffer )
+ {
+ Q_memcpy( pubEncryptedData, pTemp, *pcubEncryptedData );
+ }
+ Q_memset( pubEncryptedData + *pcubEncryptedData, 0, (cubEncryptedData - *pcubEncryptedData ) );
+ bRet = true;
+ }
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::SymmetricEncrypt: crypto++ threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ if ( bUseTempBuffer )
+ {
+ delete[] pTemp;
+ }
+
+ return bRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Encrypts the specified data with the specified key. Uses AES (Rijndael) symmetric
+// encryption. The encrypted data may then be decrypted by calling SymmetricDecrypt
+// with the same key. Generates a random initialization vector of the
+// appropriate size.
+// Input: pubPlaintextData - Data to be encrypted
+// cubPlaintextData - Size of data to be encrypted
+// pubEncryptedData - Pointer to buffer to receive encrypted data
+// pcubEncryptedData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for encrypted data. When the method returns, this will contain
+// the actual size of the encrypted data.
+// pubKey - the key to encrypt the data with
+// cubKey - Size of the key (must be k_nSymmetricKeyLen)
+// Output: true if successful, false if encryption failed
+//-----------------------------------------------------------------------------
+
+bool CCrypto::SymmetricEncrypt( const uint8 *pubPlaintextData, const uint32 cubPlaintextData,
+ uint8 *pubEncryptedData, uint32 *pcubEncryptedData,
+ const uint8 *pubKey, const uint32 cubKey )
+{
+ bool bRet = false;
+
+ //
+ // Generate a random IV
+ //
+ AESEncryption aesEncrypt( pubKey, cubKey );
+ byte rgubIV[k_cMedBuff];
+ CPoolAllocatedRNG rng;
+ rng.GetRNG().GenerateBlock( rgubIV, aesEncrypt.BlockSize() );
+
+ bRet = SymmetricEncryptWithIV( pubPlaintextData, cubPlaintextData, rgubIV, aesEncrypt.BlockSize(), pubEncryptedData, pcubEncryptedData, pubKey, cubKey );
+
+ return bRet;
+}
+
+
+#ifdef USE_OPENSSL_AES_DECRYPT
+
+// Local helper to perform AES+CBC decryption using optimized OpenSSL AES routines
+static bool BDecryptAESUsingOpenSSL( const uint8 *pubEncryptedData, uint32 cubEncryptedData, uint8 *pubPlaintextData, uint32 *pcubPlaintextData, AES_KEY *key, const uint8 *pIV )
+{
+ COMPILE_TIME_ASSERT( k_nSymmetricBlockSize == 16 );
+
+ // Block cipher encrypted text must be a multiple of the block size
+ if ( cubEncryptedData % k_nSymmetricBlockSize != 0 )
+ return false;
+
+ // Enough input? Requirement is one padded final block
+ if ( cubEncryptedData < k_nSymmetricBlockSize )
+ return false;
+
+ // Enough output space for all the full non-final blocks?
+ if ( *pcubPlaintextData < cubEncryptedData - k_nSymmetricBlockSize )
+ return false;
+
+ uint8 rgubWorking[k_nSymmetricBlockSize];
+ uint32 nDecrypted = 0;
+
+ // Process non-final blocks
+#if defined(_M_IX86) || defined (_M_X64) || defined(__i386__) || defined(__x86_64__)
+ // ... believe it or not, Steam client on Windows supports Athlon XP without SSE2
+ if ( !IsWindows() || GetCPUInformation().m_bSSE2 )
+ {
+ while ( nDecrypted < cubEncryptedData - k_nSymmetricBlockSize )
+ {
+ AES_decrypt( pubEncryptedData + nDecrypted, rgubWorking, key );
+ __m128i m128Temp = _mm_xor_si128( _mm_loadu_si128( (__m128i*)pIV ), _mm_loadu_si128( (__m128i*)rgubWorking ) );
+ pIV = pubEncryptedData + nDecrypted;
+ nDecrypted += k_nSymmetricBlockSize;
+ _mm_storeu_si128( (__m128i* RESTRICT)( pubPlaintextData + nDecrypted - k_nSymmetricBlockSize ), m128Temp );
+ }
+ }
+ else
+#endif
+ {
+ while ( nDecrypted < cubEncryptedData - k_nSymmetricBlockSize )
+ {
+ AES_decrypt( pubEncryptedData + nDecrypted, rgubWorking, key );
+ for ( int i = 0; i < k_nSymmetricBlockSize; ++i )
+ pubPlaintextData[nDecrypted + i] = rgubWorking[i] ^ pIV[i];
+ pIV = pubEncryptedData + nDecrypted;
+ nDecrypted += k_nSymmetricBlockSize;
+ }
+ }
+
+ // Process final block into rgubWorking for padding inspection
+ Assert( nDecrypted == cubEncryptedData - k_nSymmetricBlockSize );
+ AES_decrypt( pubEncryptedData + nDecrypted, rgubWorking, key );
+ for ( int i = 0; i < k_nSymmetricBlockSize; ++i )
+ rgubWorking[i] ^= pIV[i];
+
+ // Get final block padding length and make sure it is backfilled properly (PKCS#5)
+ uint8 pad = rgubWorking[ k_nSymmetricBlockSize - 1 ];
+ if ( pad < 1 || pad > k_nSymmetricBlockSize )
+ return false;
+ for ( int i = k_nSymmetricBlockSize - pad; i < k_nSymmetricBlockSize; ++i )
+ if ( rgubWorking[i] != pad )
+ return false;
+
+ // Check that we have enough space for final bytes
+ if ( *pcubPlaintextData < nDecrypted + k_nSymmetricBlockSize - pad )
+ return false;
+
+ // Write any non-pad bytes from rgubWorking to pubPlaintextData
+ for ( int i = 0; i < k_nSymmetricBlockSize - pad; ++i )
+ pubPlaintextData[nDecrypted++] = rgubWorking[i];
+
+ // The old CryptoPP path zeros out the entire destination buffer, but that
+ // behavior isn't documented or even expected. We'll just zero out one byte
+ // in case anyone relies on string termination, but that zero isn't counted.
+ if ( *pcubPlaintextData > nDecrypted )
+ pubPlaintextData[nDecrypted] = 0;
+
+ *pcubPlaintextData = nDecrypted;
+ return true;
+}
+
+#else
+
+/* function, not method */
+static bool SymmetricDecryptWorker( const uint8 *pubEncryptedData, uint32 cubEncryptedData,
+ const uint8 * pIV, uint32 cubIV,
+ uint8 *pubPlaintextData, uint32 *pcubPlaintextData,
+ AESDecryption &aesDecrypt )
+{
+ VPROF_BUDGET( "CCrypto::SymmetricDecrypt", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubEncryptedData );
+ Assert( cubEncryptedData);
+ Assert( pIV );
+ Assert( cubIV );
+ Assert( pubPlaintextData );
+ Assert( pcubPlaintextData );
+ Assert( *pcubPlaintextData );
+
+ bool bRet = false;
+
+ uint32 cubPlaintextData = *pcubPlaintextData; // remember how big the caller's buffer is
+ bool bUseTempBuffer = false;
+ uint8* pTemp = pubPlaintextData;
+
+ //
+ // Crypto++ does not play nice with decrypting in place. If the buffers are
+ // overlapping, then allocate some temp space to use for the decryption.
+ //
+ // It does work fine with _identical_ buffers, but due to the way we store
+ // the IV in the returned encrypted data we never actually hit that case.
+ //
+ if ( ( pubEncryptedData + cubEncryptedData >= pubPlaintextData ) &&
+ ( pubPlaintextData + cubPlaintextData >= pubEncryptedData ) )
+ {
+ pTemp = new uint8[cubPlaintextData];
+ bUseTempBuffer = true;
+ }
+
+ try // handle any exceptions crypto++ may throw
+ {
+ if ( pTemp != NULL )
+ {
+ CryptoPP::ArraySink* pOutputSink = new CryptoPP::ArraySink( pTemp, *pcubPlaintextData );
+ CryptoPP::CBC_Mode_ExternalCipher::Decryption cbc( aesDecrypt, pIV );
+ // Note: StreamTransformationFilter now owns the pointer to pOutputSink and will
+ // free it when the filter goes out of scope and destructs
+ CryptoPP::StreamTransformationFilter padding( cbc, pOutputSink );
+ padding.Put( pubEncryptedData, cubEncryptedData );
+ padding.MessageEnd();
+ // return length of decrypted data to caller
+ *pcubPlaintextData = pOutputSink->TotalPutLength();
+ // CryptoPP may leave garbage hanging around in the caller's buffer past the stated output length.
+ // Just to be safe, zero out caller's buffer from end out output to end of max buffer
+ if ( bUseTempBuffer )
+ {
+ Q_memcpy( pubPlaintextData, pTemp, *pcubPlaintextData );
+ }
+ Q_memset( pubPlaintextData + *pcubPlaintextData, 0, (cubPlaintextData - *pcubPlaintextData ) );
+
+ bRet = true;
+ }
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 4, "CCrypto::SymmetricDecrypt: crypto++ threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ if ( bUseTempBuffer )
+ {
+ delete[] pTemp;
+ }
+
+ return bRet;
+}
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decrypts the specified data with the specified key. Uses AES (Rijndael) symmetric
+// decryption.
+// Input: pubEncryptedData - Data to be decrypted
+// cubEncryptedData - Size of data to be decrypted
+// pubPlaintextData - Pointer to buffer to receive decrypted data
+// pcubPlaintextData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for decrypted data. When the method returns, this will contain
+// the actual size of the decrypted data.
+// pubKey - the key to decrypt the data with
+// cubKey - Size of the key (must be k_nSymmetricKeyLen)
+// Output: true if successful, false if decryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::SymmetricDecrypt( const uint8 *pubEncryptedData, uint32 cubEncryptedData,
+ uint8 *pubPlaintextData, uint32 *pcubPlaintextData,
+ const uint8 *pubKey, const uint32 cubKey )
+{
+ Assert( pubEncryptedData );
+ Assert( cubEncryptedData);
+ Assert( pubPlaintextData );
+ Assert( pcubPlaintextData );
+ Assert( *pcubPlaintextData );
+ Assert( pubKey );
+ Assert( k_nSymmetricKeyLen == cubKey ); // the only key length supported is k_nSymmetricKeyLen
+
+ // the initialization vector (IV) must be stored in the first block of bytes.
+ // If the size of encrypted data is not at least the block size, it is not valid
+ if ( cubEncryptedData < k_nSymmetricBlockSize )
+ return false;
+
+#ifdef USE_OPENSSL_AES_DECRYPT
+
+ AES_KEY key;
+ if ( AES_set_decrypt_key( pubKey, cubKey * 8, &key ) < 0 )
+ return false;
+
+ // Our first block is straight AES block encryption of IV with user key, no XOR.
+ uint8 rgubIV[ k_nSymmetricBlockSize ];
+ AES_decrypt( pubEncryptedData, rgubIV, &key );
+ pubEncryptedData += k_nSymmetricBlockSize;
+ cubEncryptedData -= k_nSymmetricBlockSize;
+
+ return BDecryptAESUsingOpenSSL( pubEncryptedData, cubEncryptedData, pubPlaintextData, pcubPlaintextData, &key, rgubIV );
+
+#else
+
+ AESDecryption aesDecrypt( pubKey, cubKey );
+ Assert( k_nSymmetricBlockSize == aesDecrypt.BlockSize() );
+
+ // Decrypt the IV
+ byte rgubIV[k_cMedBuff];
+ Assert( Q_ARRAYSIZE( rgubIV ) >= aesDecrypt.BlockSize() );
+ aesDecrypt.ProcessBlock( pubEncryptedData, rgubIV );
+
+ // We have now consumed the IV, so remove it from the front of the message
+ pubEncryptedData += k_nSymmetricBlockSize;
+ cubEncryptedData -= k_nSymmetricBlockSize;
+
+ // given the IV stored in the message, and the key, decrypt the message
+ return SymmetricDecryptWorker( pubEncryptedData, cubEncryptedData,
+ rgubIV, aesDecrypt.BlockSize(),
+ pubPlaintextData, pcubPlaintextData,
+ aesDecrypt );
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Decrypts the specified data with the specified key. Uses AES (Rijndael) symmetric
+// decryption.
+// Input: pubEncryptedData - Data to be decrypted
+// cubEncryptedData - Size of data to be decrypted
+// pIV - Initialization vector. Byte array one block in size.
+// cubIV - size of IV. This should be 16 (one block, 128 bits)
+// pubPlaintextData - Pointer to buffer to receive decrypted data
+// pcubPlaintextData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for decrypted data. When the method returns, this will contain
+// the actual size of the decrypted data.
+// pubKey - the key to decrypt the data with
+// cubKey - Size of the key (must be k_nSymmetricKeyLen)
+// Output: true if successful, false if decryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::SymmetricDecryptWithIV( const uint8 *pubEncryptedData, uint32 cubEncryptedData,
+ const uint8 * pIV, uint32 cubIV,
+ uint8 *pubPlaintextData, uint32 *pcubPlaintextData,
+ const uint8 *pubKey, const uint32 cubKey )
+{
+ Assert( pubEncryptedData );
+ Assert( cubEncryptedData);
+ Assert( pIV );
+ Assert( cubIV );
+ Assert( pubPlaintextData );
+ Assert( pcubPlaintextData );
+ Assert( *pcubPlaintextData );
+ Assert( pubKey );
+ Assert( k_nSymmetricKeyLen == cubKey ); // the only key length supported is k_nSymmetricKeyLen
+
+ // IV input into CBC must be exactly one block size
+ if ( cubIV != k_nSymmetricBlockSize )
+ return false;
+
+#ifdef USE_OPENSSL_AES_DECRYPT
+
+ AES_KEY key;
+ if ( AES_set_decrypt_key( pubKey, cubKey * 8, &key ) < 0 )
+ return false;
+
+ return BDecryptAESUsingOpenSSL( pubEncryptedData, cubEncryptedData, pubPlaintextData, pcubPlaintextData, &key, pIV );
+
+#else
+
+ AESDecryption aesDecrypt( pubKey, cubKey );
+ Assert( k_nSymmetricBlockSize == aesDecrypt.BlockSize() );
+
+ return SymmetricDecryptWorker( pubEncryptedData, cubEncryptedData,
+ pIV, cubIV,
+ pubPlaintextData, pcubPlaintextData,
+ aesDecrypt );
+
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: For specified plaintext data size, returns what size of symmetric
+// encrypted data will be
+//-----------------------------------------------------------------------------
+uint32 CCrypto::GetSymmetricEncryptedSize( uint32 cubPlaintextData )
+{
+ // empirically determined encrypted size as function of plaintext size for AES encryption
+ uint k_cubBlock = 16;
+ uint k_cubHeader = 16;
+ return k_cubHeader + ( ( cubPlaintextData / k_cubBlock ) * k_cubBlock ) + k_cubBlock;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Encrypts the specified data with the specified text password.
+// Uses the SHA256 hash of the password as the key for AES (Rijndael) symmetric
+// encryption. A SHA1 HMAC of the result is appended, for authentication on
+// the receiving end.
+// The encrypted data may then be decrypted by calling DecryptWithPasswordAndAuthenticate
+// with the same password.
+// Input: pubPlaintextData - Data to be encrypted
+// cubPlaintextData - Size of data to be encrypted
+// pubEncryptedData - Pointer to buffer to receive encrypted data
+// pcubEncryptedData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for encrypted data. When the method returns, this will contain
+// the actual size of the encrypted data.
+// pchPassword - text password
+// Output: true if successful, false if encryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::EncryptWithPasswordAndHMAC( const uint8 *pubPlaintextData, uint32 cubPlaintextData,
+ uint8 * pubEncryptedData, uint32 * pcubEncryptedData,
+ const char *pchPassword )
+{
+ //
+ // Generate a random IV
+ //
+ byte rgubIV[k_nSymmetricBlockSize];
+ CPoolAllocatedRNG rng;
+ rng.GetRNG().GenerateBlock( rgubIV, k_nSymmetricBlockSize );
+
+ return EncryptWithPasswordAndHMACWithIV( pubPlaintextData, cubPlaintextData, rgubIV, k_nSymmetricBlockSize, pubEncryptedData, pcubEncryptedData, pchPassword );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Encrypts the specified data with the specified text password.
+// Uses the SHA256 hash of the password as the key for AES (Rijndael) symmetric
+// encryption. A SHA1 HMAC of the result is appended, for authentication on
+// the receiving end.
+// The encrypted data may then be decrypted by calling DecryptWithPasswordAndAuthenticate
+// with the same password.
+// Input: pubPlaintextData - Data to be encrypted
+// cubPlaintextData - Size of data to be encrypted
+// pIV - IV to use for AES encryption. Should be random and never used before unless you know
+// exactly what you're doing.
+// cubIV - size of the IV - should be same ase the AES blocksize.
+// pubEncryptedData - Pointer to buffer to receive encrypted data
+// pcubEncryptedData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for encrypted data. When the method returns, this will contain
+// the actual size of the encrypted data.
+// pchPassword - text password
+// Output: true if successful, false if encryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::EncryptWithPasswordAndHMACWithIV( const uint8 *pubPlaintextData, uint32 cubPlaintextData,
+ const uint8 * pIV, uint32 cubIV,
+ uint8 * pubEncryptedData, uint32 * pcubEncryptedData,
+ const char *pchPassword )
+{
+ uint8 rgubKey[k_nSymmetricKeyLen];
+ if ( !pchPassword || !pchPassword[0] )
+ return false;
+
+ if ( !cubPlaintextData )
+ return false;
+
+ uint32 cubBuffer = *pcubEncryptedData;
+ uint32 cubExpectedResult = GetSymmetricEncryptedSize( cubPlaintextData ) + sizeof( SHADigest_t );
+
+ if ( cubBuffer < cubExpectedResult )
+ return false;
+
+ try
+ {
+ CryptoPP::SHA256().CalculateDigest( rgubKey, (const uint8 *)pchPassword, Q_strlen( pchPassword ) );
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 4, "CCrypto::EncryptWithPassword: crypto++ threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ return false;
+ }
+
+ bool bRet = SymmetricEncryptWithIV( pubPlaintextData, cubPlaintextData, pIV, cubIV, pubEncryptedData, pcubEncryptedData, rgubKey, k_nSymmetricKeyLen );
+ if ( bRet )
+ {
+ // calc HMAC
+ uint32 cubEncrypted = *pcubEncryptedData;
+ *pcubEncryptedData += sizeof( SHADigest_t );
+ if ( cubBuffer < *pcubEncryptedData )
+ return false;
+
+ SHADigest_t *pHMAC = (SHADigest_t*)( pubEncryptedData + cubEncrypted );
+ bRet = CCrypto::GenerateHMAC( pubEncryptedData, cubEncrypted, rgubKey, k_nSymmetricKeyLen, pHMAC );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decrypts the specified data with the specified password. Uses AES (Rijndael) symmetric
+// decryption. First, the HMAC is verified - if it is not correct, then we know that
+// the key is incorrect or the data is corrupted, and the decryption fails.
+// Input: pubEncryptedData - Data to be decrypted
+// cubEncryptedData - Size of data to be decrypted
+// pubPlaintextData - Pointer to buffer to receive decrypted data
+// pcubPlaintextData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for decrypted data. When the method returns, this will contain
+// the actual size of the decrypted data.
+// pchPassword - the text password to decrypt the data with
+// Output: true if successful, false if decryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::DecryptWithPasswordAndAuthenticate( const uint8 * pubEncryptedData, uint32 cubEncryptedData,
+ uint8 * pubPlaintextData, uint32 * pcubPlaintextData,
+ const char *pchPassword )
+{
+ uint8 rgubKey[k_nSymmetricKeyLen];
+ if ( !pchPassword || !pchPassword[0] )
+ return false;
+
+ if ( cubEncryptedData <= sizeof( SHADigest_t ) )
+ return false;
+
+ try
+ {
+ CryptoPP::SHA256().CalculateDigest( rgubKey, (const uint8 *)pchPassword, Q_strlen( pchPassword ) );
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 4, "CCrypto::EncryptWithPassword: crypto++ threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ return false;
+ }
+
+ uint32 cubCiphertext = cubEncryptedData - sizeof( SHADigest_t );
+ SHADigest_t *pHMAC = (SHADigest_t*)( pubEncryptedData + cubCiphertext );
+ SHADigest_t hmacActual;
+ bool bRet = CCrypto::GenerateHMAC( pubEncryptedData, cubCiphertext, rgubKey, k_nSymmetricKeyLen, &hmacActual );
+
+ if ( bRet )
+ {
+ // invalid ciphertext or key
+ if ( Q_memcmp( &hmacActual, pHMAC, sizeof( SHADigest_t ) ) )
+ return false;
+
+ bRet = SymmetricDecrypt( pubEncryptedData, cubCiphertext, pubPlaintextData, pcubPlaintextData, rgubKey, k_nSymmetricKeyLen );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates a new pair of private/public RSA keys
+// Input: pubPublicKey - Pointer to buffer to receive public key (should be of size k_nRSAKeyLenMax)
+// pcubPublicKey - Pointer to variable that contains size of pubPublicKey buffer. At exit,
+// this is filled in with the actual size of the public key
+// pubPrivateKey - Pointer to buffer to receive private key (should be of size k_nRSAKeyLenMax)
+// pcubPrivateKey - Pointer to variable that contains size of pubPrivateKey buffer. At exit,
+// this is filled in with the actual size of the private key
+// Output: true if successful, false if key generation failed
+//-----------------------------------------------------------------------------
+bool CCrypto::RSAGenerateKeys( uint8 *pubPublicKey, uint32 *pcubPublicKey, uint8 *pubPrivateKey, uint32 *pcubPrivateKey )
+{
+ VPROF_BUDGET( "CCrypto::RSAGenerateKeys", VPROF_BUDGETGROUP_ENCRYPTION );
+ bool bRet = false;
+ Assert( pubPublicKey );
+ Assert( pcubPublicKey );
+ Assert( pubPrivateKey );
+ Assert( pcubPrivateKey );
+
+ try // handle any exceptions crypto++ may throw
+ {
+ // generate private key
+ ArraySink arraySinkPrivateKey( pubPrivateKey, *pcubPrivateKey );
+ CPoolAllocatedRNG rng;
+ RSAES_OAEP_SHA_Decryptor priv( rng.GetRNG(), k_nRSAKeyBits );
+ priv.DEREncode( arraySinkPrivateKey );
+ *pcubPrivateKey = arraySinkPrivateKey.TotalPutLength();
+ // generate public key
+ ArraySink arraySinkPublicKey( pubPublicKey, *pcubPublicKey );
+ RSAES_OAEP_SHA_Encryptor pub(priv);
+ pub.DEREncode( arraySinkPublicKey );
+ *pcubPublicKey = arraySinkPublicKey.TotalPutLength();
+ bRet = true;
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSAGenerateKeys: crypto++ threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Encrypts the specified data with the specified RSA public key.
+// The encrypted data may then be decrypted by calling RSADecrypt with the
+// corresponding RSA private key.
+// Input: pubPlaintextData - Data to be encrypted
+// cubPlaintextData - Size of data to be encrypted
+// pubEncryptedData - Pointer to buffer to receive encrypted data
+// pcubEncryptedData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for encrypted data. When the method returns, this will contain
+// the actual size of the encrypted data.
+// pubPublicKey - the RSA public key to encrypt the data with
+// cubPublicKey - Size of the key (must be k_nSymmetricKeyLen)
+// Output: true if successful, false if encryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::RSAEncrypt( const uint8 *pubPlaintextData, uint32 cubPlaintextData,
+ uint8 *pubEncryptedData, uint32 *pcubEncryptedData,
+ const uint8 *pubPublicKey, const uint32 cubPublicKey )
+{
+ VPROF_BUDGET( "CCrypto::RSAEncrypt", VPROF_BUDGETGROUP_ENCRYPTION );
+ bool bRet = false;
+ Assert( cubPlaintextData > 0 ); // must pass in some data
+
+ try // handle any exceptions crypto++ may throw
+ {
+ StringSource stringSourcePublicKey( pubPublicKey, cubPublicKey, true );
+ RSAES_OAEP_SHA_Encryptor rsaEncryptor( stringSourcePublicKey );
+
+ // calculate how many blocks of encryption will we need to do
+ AssertFatal( rsaEncryptor.FixedMaxPlaintextLength() <= ULONG_MAX );
+ uint32 cBlocks = 1 + ( ( cubPlaintextData - 1 ) / (uint32)rsaEncryptor.FixedMaxPlaintextLength() );
+ // calculate how big the output will be
+ AssertFatal( rsaEncryptor.FixedCiphertextLength() <= ULONG_MAX / cBlocks );
+ uint32 cubCipherText = cBlocks * (uint32)rsaEncryptor.FixedCiphertextLength();
+ Assert( cubCipherText > 0 );
+
+ // ensure there is sufficient room in output buffer for result
+ if ( cubCipherText > ( *pcubEncryptedData ) )
+ {
+ AssertMsg2( false, "CCrypto::RSAEncrypt: insufficient output buffer for encryption, needed %d got %d\n",
+ cubCipherText, *pcubEncryptedData );
+ return false;
+ }
+
+ // encrypt the message, using as many blocks as required
+ CPoolAllocatedRNG rng;
+ for ( uint32 nBlock = 0; nBlock < cBlocks; nBlock++ )
+ {
+ // encrypt either all remaining plaintext, or maximum allowed plaintext per RSA encryption operation
+ uint32 cubToEncrypt = min( cubPlaintextData, (uint32)rsaEncryptor.FixedMaxPlaintextLength() );
+ // encrypt the plaintext
+ rsaEncryptor.Encrypt( rng.GetRNG(), pubPlaintextData, cubToEncrypt, pubEncryptedData );
+ // adjust input and output pointers and remaining plaintext byte count
+ pubPlaintextData += cubToEncrypt;
+ cubPlaintextData -= cubToEncrypt;
+ pubEncryptedData += rsaEncryptor.FixedCiphertextLength();
+ }
+ Assert( 0 == cubPlaintextData ); // should have no remaining plaintext to encrypt
+ *pcubEncryptedData = cubCipherText;
+
+ bRet = true;
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSAEncrypt: Encrypt() threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decrypts the specified data with the specified RSA private key
+// Input: pubEncryptedData - Data to be decrypted
+// cubEncryptedData - Size of data to be decrypted
+// pubPlaintextData - Pointer to buffer to receive decrypted data
+// pcubPlaintextData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for decrypted data. When the method returns, this will contain
+// the actual size of the decrypted data.
+// pubPrivateKey - the RSA private key key to decrypt the data with
+// cubPrivateKey - Size of the key (must be k_nSymmetricKeyLen)
+// Output: true if successful, false if decryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::RSADecrypt( const uint8 *pubEncryptedData, uint32 cubEncryptedData,
+ uint8 *pubPlaintextData, uint32 *pcubPlaintextData,
+ const uint8 *pubPrivateKey, const uint32 cubPrivateKey )
+{
+ VPROF_BUDGET( "CCrypto::RSADecrypt", VPROF_BUDGETGROUP_ENCRYPTION );
+ bool bRet = false;
+
+ Assert( cubEncryptedData > 0 ); // must pass in some data
+
+ try // handle any exceptions crypto++ may throw
+ {
+ StringSource stringSourcePrivateKey( pubPrivateKey, cubPrivateKey, true );
+ RSAES_OAEP_SHA_Decryptor rsaDecryptor( stringSourcePrivateKey );
+
+ // calculate how many blocks of decryption will we need to do
+ AssertFatal( rsaDecryptor.FixedCiphertextLength() <= ULONG_MAX );
+ uint32 cubFixedCiphertextLength = (uint32)rsaDecryptor.FixedCiphertextLength();
+ // Ensure encrypted data is valid and has length that is exact multiple of 128 bytes
+ if ( 0 != ( cubEncryptedData % cubFixedCiphertextLength ) )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSADecrypt: invalid ciphertext length %d, needs to be a multiple of %d\n",
+ cubEncryptedData, cubFixedCiphertextLength );
+ return false;
+ }
+ uint32 cBlocks = cubEncryptedData / cubFixedCiphertextLength;
+
+ // calculate how big the maximum output will be
+ size_t cubMaxPlaintext = rsaDecryptor.MaxPlaintextLength( rsaDecryptor.FixedCiphertextLength() );
+ AssertFatal( cubMaxPlaintext <= ULONG_MAX / cBlocks );
+ uint32 cubPlaintextDataMax = cBlocks * (uint32)cubMaxPlaintext;
+ Assert( cubPlaintextDataMax > 0 );
+ // ensure there is sufficient room in output buffer for result
+ if ( cubPlaintextDataMax >= ( *pcubPlaintextData ) )
+ {
+ AssertMsg2( false, "CCrypto::RSADecrypt: insufficient output buffer for decryption, needed %d got %d\n",
+ cubPlaintextDataMax, *pcubPlaintextData );
+ return false;
+ }
+
+ // decrypt the data, using as many blocks as required
+ CPoolAllocatedRNG rng;
+ uint32 cubPlaintextData = 0;
+ for ( uint32 nBlock = 0; nBlock < cBlocks; nBlock++ )
+ {
+ // decrypt one block (always of fixed size)
+ int cubToDecrypt = cubFixedCiphertextLength;
+ DecodingResult decodingResult = rsaDecryptor.Decrypt( rng.GetRNG(), pubEncryptedData, cubToDecrypt, pubPlaintextData );
+ if ( !decodingResult.isValidCoding )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSADecrypt: failed to decrypt\n" );
+ return false;
+ }
+ // adjust input and output pointers and remaining encrypted byte count
+ pubEncryptedData += cubToDecrypt;
+ cubEncryptedData -= cubToDecrypt;
+ pubPlaintextData += decodingResult.messageLength;
+ AssertFatal( decodingResult.messageLength <= ULONG_MAX );
+ cubPlaintextData += (uint32)decodingResult.messageLength;
+ }
+ Assert( 0 == cubEncryptedData ); // should have no remaining encrypted data to decrypt
+ *pcubPlaintextData = cubPlaintextData;
+
+ bRet = true;
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSADecrypt: Decrypt() threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decrypts the specified data with the specified RSA PUBLIC key,
+// using no padding (eg un-padded signature).
+// Input: pubEncryptedData - Data to be decrypted
+// cubEncryptedData - Size of data to be decrypted
+// pubPlaintextData - Pointer to buffer to receive decrypted data
+// pcubPlaintextData - Pointer to a variable that at time of call contains the size of
+// the receive buffer for decrypted data. When the method returns, this will contain
+// the actual size of the decrypted data.
+// pubPublicKey - the RSA public key key to decrypt the data with
+// cubPublicKey - Size of the key
+// Output: true if successful, false if decryption failed
+//-----------------------------------------------------------------------------
+bool CCrypto::RSAPublicDecrypt_NoPadding( const uint8 *pubEncryptedData, uint32 cubEncryptedData,
+ uint8 *pubPlaintextData, uint32 *pcubPlaintextData,
+ const uint8 *pubPublicKey, const uint32 cubPublicKey )
+{
+ VPROF_BUDGET( "CCrypto::RSADecrypt", VPROF_BUDGETGROUP_ENCRYPTION );
+ bool bRet = false;
+
+ Assert( cubEncryptedData > 0 ); // must pass in some data
+
+ // BUGBUG taylor
+ // This probably only works for reasonably small ciphertext sizes.
+
+ try // handle any exceptions crypto++-5.2 may throw
+ {
+ StringSource stringSourcePublicKey( pubPublicKey, cubPublicKey, true );
+
+ // 1. We need to use a Verifier because a Decryptor expects a private key,
+ // which is encoded differently
+ // 2. We are using neither PKCS1v15 padding nor SHA in any way, we are simply
+ // using this object as a means of instantiating the key decryption function
+ RSASSA_PKCS1v15_SHA_Verifier pub( stringSourcePublicKey );
+
+ // Ask for the data to be decrypted
+ // Caveat: this may "succeed" even if the ciphertext is bogus, so it
+ // is up to the caller to do any MAC or other sanity checking
+ Integer x = pub.AccessKey().ApplyFunction(Integer(pubEncryptedData, cubEncryptedData));
+
+ // Result is an 'Integer', essentially a string of bytes for our
+ // purposes though.
+ uint32 nBytes = x.ByteCount();
+ if ( nBytes > *pcubPlaintextData )
+ {
+ return false;
+ }
+
+ // don't tell it to encode to the full buffer size, because it will
+ // pre-pad with zeros. Just squeeze it in to the first nBytes of the
+ // buffer.
+ x.Encode( pubPlaintextData, nBytes );
+ *pcubPlaintextData = nBytes;
+
+ bRet = true;
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSAPublicDecrypt_NoPadding: Decrypt() threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates an RSA signature block for the specified data with the specified
+// RSA private key. The signature can be verified by calling RSAVerifySignature
+// with the RSA public key.
+// Input: pubData - Data to be signed
+// cubData - Size of data to be signed
+// pubSignature - Pointer to buffer to receive signature block
+// pcubSignature - Pointer to a variable that at time of call contains the size of
+// the pubSignature buffer. When the method returns, this will contain
+// the actual size of the signature block
+// pubPrivateKey - The RSA private key to use to sign the data
+// cubPrivateKey - Size of the key
+// Output: true if successful, false if signature failed
+//-----------------------------------------------------------------------------
+bool CCrypto::RSASign( const uint8 *pubData, const uint32 cubData,
+ uint8 *pubSignature, uint32 *pcubSignature,
+ const uint8 *pubPrivateKey, const uint32 cubPrivateKey )
+{
+ VPROF_BUDGET( "CCrypto::RSASign", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( pubPrivateKey );
+ Assert( cubPrivateKey > 0 );
+ Assert( pubSignature );
+ Assert( pcubSignature );
+ bool bRet = false;
+ try // handle any exceptions crypto++ may throw
+ {
+ StringSource stringSourcePrivateKey( pubPrivateKey, cubPrivateKey, true );
+ RSASSA_PKCS1v15_SHA_Signer rsaSigner( stringSourcePrivateKey );
+ CPoolAllocatedRNG rng;
+ size_t len = rsaSigner.SignMessage( rng.GetRNG(), (byte *)pubData, cubData, pubSignature );
+ AssertFatal( len <= ULONG_MAX );
+ *pcubSignature = (uint32)len;
+ bRet = true;
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSASign: SignMessage threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Verifies that signature block is authentic for given data & RSA public key
+// Input: pubData - Data that was signed
+// cubData - Size of data that was signed signed
+// pubSignature - Signature block
+// cubSignature - Size of signature block
+// pubPublicKey - The RSA public key to use to verify the signature
+// (must be from same pair as RSA private key used to generate signature)
+// cubPublicKey - Size of the key
+// Output: true if successful and signature is authentic, false if signature does not match or other error
+//-----------------------------------------------------------------------------
+bool CCrypto::RSAVerifySignature( const uint8 *pubData, const uint32 cubData,
+ const uint8 *pubSignature, const uint32 cubSignature,
+ const uint8 *pubPublicKey, const uint32 cubPublicKey )
+{
+ VPROF_BUDGET( "CCrypto::RSAVerifySignature", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( pubSignature );
+ Assert( pubPublicKey );
+
+ bool bRet = false;
+ try // handle any exceptions crypto++ may throw
+ {
+ StringSource stringSourcePublicKey( pubPublicKey, cubPublicKey, true );
+ RSASSA_PKCS1v15_SHA_Verifier pub( stringSourcePublicKey );
+ bRet = pub.VerifyMessage( pubData, cubData, pubSignature, cubSignature );
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSASign: VerifyMessage threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates an RSA signature block for the specified data with the specified
+// RSA private key. The signature can be verified by calling RSAVerifySignature
+// with the RSA public key.
+// Input: pubData - Data to be signed
+// cubData - Size of data to be signed
+// pubSignature - Pointer to buffer to receive signature block
+// pcubSignature - Pointer to a variable that at time of call contains the size of
+// the pubSignature buffer. When the method returns, this will contain
+// the actual size of the signature block
+// pubPrivateKey - The RSA private key to use to sign the data
+// cubPrivateKey - Size of the key
+// Output: true if successful, false if signature failed
+//-----------------------------------------------------------------------------
+bool CCrypto::RSASignSHA256( const uint8 *pubData, const uint32 cubData,
+ uint8 *pubSignature, uint32 *pcubSignature,
+ const uint8 *pubPrivateKey, const uint32 cubPrivateKey )
+{
+ VPROF_BUDGET( "CCrypto::RSASign", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( pubPrivateKey );
+ Assert( cubPrivateKey > 0 );
+ Assert( pubSignature );
+ Assert( pcubSignature );
+ bool bRet = false;
+ try // handle any exceptions crypto++ may throw
+ {
+ StringSource stringSourcePrivateKey( pubPrivateKey, cubPrivateKey, true );
+ RSASS<PKCS1v15, SHA256>::Signer rsaSigner( stringSourcePrivateKey );
+ CPoolAllocatedRNG rng;
+ size_t len = rsaSigner.SignMessage( rng.GetRNG(), (byte *)pubData, cubData, pubSignature );
+ AssertFatal( len <= ULONG_MAX );
+ *pcubSignature = (uint32)len;
+ bRet = true;
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSASign: SignMessage threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Verifies that signature block is authentic for given data & RSA public key
+// Input: pubData - Data that was signed
+// cubData - Size of data that was signed signed
+// pubSignature - Signature block
+// cubSignature - Size of signature block
+// pubPublicKey - The RSA public key to use to verify the signature
+// (must be from same pair as RSA private key used to generate signature)
+// cubPublicKey - Size of the key
+// Output: true if successful and signature is authentic, false if signature does not match or other error
+//-----------------------------------------------------------------------------
+bool CCrypto::RSAVerifySignatureSHA256( const uint8 *pubData, const uint32 cubData,
+ const uint8 *pubSignature, const uint32 cubSignature,
+ const uint8 *pubPublicKey, const uint32 cubPublicKey )
+{
+ VPROF_BUDGET( "CCrypto::RSAVerifySignature", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( pubSignature );
+ Assert( pubPublicKey );
+
+ bool bRet = false;
+ try // handle any exceptions crypto++ may throw
+ {
+ StringSource stringSourcePublicKey( pubPublicKey, cubPublicKey, true );
+ RSASS<PKCS1v15, SHA256>::Verifier pub( stringSourcePublicKey );
+ bRet = pub.VerifyMessage( pubData, cubData, pubSignature, cubSignature );
+ }
+ catch ( Exception e )
+ {
+ DMsg( SPEW_CRYPTO, 2, "CCrypto::RSASign: VerifyMessage threw exception %s (%d)\n",
+ e.what(), e.GetErrorType() );
+ }
+
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hex-encodes a block of data. (Binary -> text representation.) The output
+// is null-terminated and can be treated as a string.
+// Input: pubData - Data to encode
+// cubData - Size of data to encode
+// pchEncodedData - Pointer to string buffer to store output in
+// cchEncodedData - Size of pchEncodedData buffer
+//-----------------------------------------------------------------------------
+bool CCrypto::HexEncode( const uint8 *pubData, const uint32 cubData, char *pchEncodedData, uint32 cchEncodedData )
+{
+ VPROF_BUDGET( "CCrypto::HexEncode", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( cubData );
+ Assert( pchEncodedData );
+ Assert( cchEncodedData > 0 );
+
+ if ( cchEncodedData < ( ( cubData * 2 ) + 1 ) )
+ {
+ Assert( cchEncodedData >= ( cubData * 2 ) + 1 ); // expands to 2x input + NULL, must have room in output buffer
+ *pchEncodedData = '\0';
+ return false;
+ }
+
+ ArraySink * pArraySinkOutput = new ArraySink( (byte *) pchEncodedData, cchEncodedData );
+ // Note: HexEncoder now owns the pointer to pOutputSink and will free it when the encoder goes out of scope and destructs
+ HexEncoder hexEncoder( pArraySinkOutput );
+ hexEncoder.Put( pubData, cubData );
+ hexEncoder.MessageEnd();
+
+ uint32 len = pArraySinkOutput->TotalPutLength();
+ if ( len >= cchEncodedData )
+ {
+ AssertMsg2( false, "CCrypto::HexEncode: insufficient output buffer for encoding, needed %d got %d\n",
+ len, cchEncodedData );
+ return false;
+ }
+
+ pchEncodedData[len] = 0; // NULL-terminate
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hex-decodes a block of data. (Text -> binary representation.)
+// Input: pchData - Null-terminated hex-encoded string
+// pubDecodedData - Pointer to buffer to store output in
+// pcubDecodedData - Pointer to variable that contains size of
+// output buffer. At exit, is filled in with actual size
+// of decoded data.
+//-----------------------------------------------------------------------------
+bool CCrypto::HexDecode( const char *pchData, uint8 *pubDecodedData, uint32 *pcubDecodedData )
+{
+ VPROF_BUDGET( "CCrypto::HexDecode", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pchData );
+ Assert( pubDecodedData );
+ Assert( pcubDecodedData );
+ Assert( *pcubDecodedData );
+
+ ArraySink * pArraySinkOutput = new ArraySink( pubDecodedData, *pcubDecodedData );
+ // Note: HexEncoder now owns the pointer to pOutputSink and will free it when the encoder goes out of scope and destructs
+ HexDecoder hexDecoder( pArraySinkOutput );
+ hexDecoder.Put( (byte *) pchData, Q_strlen( pchData ) );
+ hexDecoder.MessageEnd();
+
+ uint32 len = pArraySinkOutput->TotalPutLength();
+ if ( len > *pcubDecodedData )
+ {
+ AssertMsg2( false, "CCrypto::HexDecode: insufficient output buffer for decoding, needed %d got %d\n",
+ len, *pcubDecodedData );
+ return false;
+ }
+ *pcubDecodedData = len;
+
+ return true;
+}
+
+
+static const int k_LineBreakEveryNGroups = 18; // line break every 18 groups of 4 characters (every 72 characters)
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the expected buffer size that should be passed to Base64Encode.
+// Input: cubData - Size of data to encode
+// bInsertLineBreaks - If line breaks should be inserted automatically
+//-----------------------------------------------------------------------------
+uint32 CCrypto::Base64EncodeMaxOutput( const uint32 cubData, const char *pszLineBreak )
+{
+ // terminating null + 4 chars per 3-byte group + line break after every 18 groups (72 output chars) + final line break
+ uint32 nGroups = (cubData+2)/3;
+ str_size cchRequired = 1 + nGroups*4 + ( pszLineBreak ? Q_strlen(pszLineBreak)*(1+(nGroups-1)/k_LineBreakEveryNGroups) : 0 );
+ return cchRequired;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base64-encodes a block of data. (Binary -> text representation.) The output
+// is null-terminated and can be treated as a string.
+// Input: pubData - Data to encode
+// cubData - Size of data to encode
+// pchEncodedData - Pointer to string buffer to store output in
+// cchEncodedData - Size of pchEncodedData buffer
+// bInsertLineBreaks - If "\n" line breaks should be inserted automatically
+//-----------------------------------------------------------------------------
+bool CCrypto::Base64Encode( const uint8 *pubData, uint32 cubData, char *pchEncodedData, uint32 cchEncodedData, bool bInsertLineBreaks )
+{
+ const char *pszLineBreak = bInsertLineBreaks ? "\n" : NULL;
+ uint32 cchRequired = Base64EncodeMaxOutput( cubData, pszLineBreak );
+ (void)cchRequired;
+ AssertMsg2( cchEncodedData >= cchRequired, "CCrypto::Base64Encode: insufficient output buffer for encoding, needed %d got %d\n", cchRequired, cchEncodedData );
+ return Base64Encode( pubData, cubData, pchEncodedData, &cchEncodedData, pszLineBreak );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Base64-encodes a block of data. (Binary -> text representation.) The output
+// is null-terminated and can be treated as a string.
+// Input: pubData - Data to encode
+// cubData - Size of data to encode
+// pchEncodedData - Pointer to string buffer to store output in
+// pcchEncodedData - Pointer to size of pchEncodedData buffer; adjusted to number of characters written (before NULL)
+// pszLineBreak - String to be inserted every 72 characters; empty string or NULL pointer for no line breaks
+// Note: if pchEncodedData is NULL and *pcchEncodedData is zero, *pcchEncodedData is filled with the actual required length
+// for output. A simpler approximation for maximum output size is (cubData * 4 / 3) + 5 if there are no linebreaks.
+//-----------------------------------------------------------------------------
+bool CCrypto::Base64Encode( const uint8 *pubData, uint32 cubData, char *pchEncodedData, uint32* pcchEncodedData, const char *pszLineBreak )
+{
+ VPROF_BUDGET( "CCrypto::Base64Encode", VPROF_BUDGETGROUP_ENCRYPTION );
+
+ if ( pchEncodedData == NULL )
+ {
+ AssertMsg( *pcchEncodedData == 0, "NULL output buffer with non-zero size passed to Base64Encode" );
+ *pcchEncodedData = Base64EncodeMaxOutput( cubData, pszLineBreak );
+ return true;
+ }
+
+ const uint8 *pubDataEnd = pubData + cubData;
+ char *pchEncodedDataStart = pchEncodedData;
+ str_size unLineBreakLen = pszLineBreak ? Q_strlen( pszLineBreak ) : 0;
+ int nNextLineBreak = unLineBreakLen ? k_LineBreakEveryNGroups : INT_MAX;
+
+ const char * const pszBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ uint32 cchEncodedData = *pcchEncodedData;
+ if ( cchEncodedData == 0 )
+ goto out_of_space;
+
+ --cchEncodedData; // pre-decrement for the terminating null so we don't forget about it
+
+ // input 3 x 8-bit, output 4 x 6-bit
+ while ( pubDataEnd - pubData >= 3 )
+ {
+ if ( cchEncodedData < 4 + unLineBreakLen )
+ goto out_of_space;
+
+ if ( nNextLineBreak == 0 )
+ {
+ memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
+ pchEncodedData += unLineBreakLen;
+ cchEncodedData -= unLineBreakLen;
+ nNextLineBreak = k_LineBreakEveryNGroups;
+ }
+
+ uint32 un24BitsData;
+ un24BitsData = (uint32) pubData[0] << 16;
+ un24BitsData |= (uint32) pubData[1] << 8;
+ un24BitsData |= (uint32) pubData[2];
+ pubData += 3;
+
+ pchEncodedData[0] = pszBase64Chars[ (un24BitsData >> 18) & 63 ];
+ pchEncodedData[1] = pszBase64Chars[ (un24BitsData >> 12) & 63 ];
+ pchEncodedData[2] = pszBase64Chars[ (un24BitsData >> 6) & 63 ];
+ pchEncodedData[3] = pszBase64Chars[ (un24BitsData ) & 63 ];
+ pchEncodedData += 4;
+ cchEncodedData -= 4;
+ --nNextLineBreak;
+ }
+
+ // Clean up remaining 1 or 2 bytes of input, pad output with '='
+ if ( pubData != pubDataEnd )
+ {
+ if ( cchEncodedData < 4 + unLineBreakLen )
+ goto out_of_space;
+
+ if ( nNextLineBreak == 0 )
+ {
+ memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
+ pchEncodedData += unLineBreakLen;
+ cchEncodedData -= unLineBreakLen;
+ }
+
+ uint32 un24BitsData;
+ un24BitsData = (uint32) pubData[0] << 16;
+ if ( pubData+1 != pubDataEnd )
+ {
+ un24BitsData |= (uint32) pubData[1] << 8;
+ }
+ pchEncodedData[0] = pszBase64Chars[ (un24BitsData >> 18) & 63 ];
+ pchEncodedData[1] = pszBase64Chars[ (un24BitsData >> 12) & 63 ];
+ pchEncodedData[2] = pubData+1 != pubDataEnd ? pszBase64Chars[ (un24BitsData >> 6) & 63 ] : '=';
+ pchEncodedData[3] = '=';
+ pchEncodedData += 4;
+ cchEncodedData -= 4;
+ }
+
+ if ( unLineBreakLen )
+ {
+ if ( cchEncodedData < unLineBreakLen )
+ goto out_of_space;
+ memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
+ pchEncodedData += unLineBreakLen;
+ cchEncodedData -= unLineBreakLen;
+ }
+
+ *pchEncodedData = 0;
+ *pcchEncodedData = pchEncodedData - pchEncodedDataStart;
+ return true;
+
+out_of_space:
+ *pchEncodedData = 0;
+ *pcchEncodedData = Base64EncodeMaxOutput( cubData, pszLineBreak );
+ AssertMsg( false, "CCrypto::Base64Encode: insufficient output buffer (up to n*4/3+5 bytes required, plus linebreaks)" );
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base64-decodes a block of data. (Text -> binary representation.)
+// Input: pchData - Null-terminated hex-encoded string
+// pubDecodedData - Pointer to buffer to store output in
+// pcubDecodedData - Pointer to variable that contains size of
+// output buffer. At exit, is filled in with actual size
+// of decoded data.
+// Note: if NULL is passed as the output buffer and *pcubDecodedData is zero, the function
+// will calculate the actual required size and place it in *pcubDecodedData. A simpler upper
+// bound on the required size is ( strlen(pchData)*3/4 + 1 ).
+//-----------------------------------------------------------------------------
+bool CCrypto::Base64Decode( const char *pchData, uint8 *pubDecodedData, uint32 *pcubDecodedData, bool bIgnoreInvalidCharacters )
+{
+ return Base64Decode( pchData, ~0u, pubDecodedData, pcubDecodedData, bIgnoreInvalidCharacters );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Base64-decodes a block of data. (Text -> binary representation.)
+// Input: pchData - base64-encoded string, null terminated
+// cchDataMax - maximum length of string unless a null is encountered first
+// pubDecodedData - Pointer to buffer to store output in
+// pcubDecodedData - Pointer to variable that contains size of
+// output buffer. At exit, is filled in with actual size
+// of decoded data.
+// Note: if NULL is passed as the output buffer and *pcubDecodedData is zero, the function
+// will calculate the actual required size and place it in *pcubDecodedData. A simpler upper
+// bound on the required size is ( strlen(pchData)*3/4 + 2 ).
+//-----------------------------------------------------------------------------
+bool CCrypto::Base64Decode( const char *pchData, uint32 cchDataMax, uint8 *pubDecodedData, uint32 *pcubDecodedData, bool bIgnoreInvalidCharacters )
+{
+ VPROF_BUDGET( "CCrypto::Base64Decode", VPROF_BUDGETGROUP_ENCRYPTION );
+
+ uint32 cubDecodedData = *pcubDecodedData;
+ uint32 cubDecodedDataOrig = cubDecodedData;
+
+ if ( pubDecodedData == NULL )
+ {
+ AssertMsg( *pcubDecodedData == 0, "NULL output buffer with non-zero size passed to Base64Decode" );
+ cubDecodedDataOrig = cubDecodedData = ~0u;
+ }
+
+ // valid base64 character range: '+' (0x2B) to 'z' (0x7A)
+ // table entries are 0-63, -1 for invalid entries, -2 for '='
+ static const char rgchInvBase64[] = {
+ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51
+ };
+ COMPILE_TIME_ASSERT( Q_ARRAYSIZE(rgchInvBase64) == 0x7A - 0x2B + 1 );
+
+ uint32 un24BitsWithSentinel = 1;
+ while ( cchDataMax-- > 0 )
+ {
+ char c = *pchData++;
+
+ if ( (uint8)(c - 0x2B) >= Q_ARRAYSIZE( rgchInvBase64 ) )
+ {
+ if ( c == '\0' )
+ break;
+
+ if ( !bIgnoreInvalidCharacters && !( c == '\r' || c == '\n' || c == '\t' || c == ' ' ) )
+ goto decode_failed;
+ else
+ continue;
+ }
+
+ c = rgchInvBase64[(uint8)(c - 0x2B)];
+ if ( c < 0 )
+ {
+ if ( c == -2 ) // -2 -> terminating '='
+ break;
+
+ if ( !bIgnoreInvalidCharacters )
+ goto decode_failed;
+ else
+ continue;
+ }
+
+ un24BitsWithSentinel <<= 6;
+ un24BitsWithSentinel |= c;
+ if ( un24BitsWithSentinel & (1<<24) )
+ {
+ if ( cubDecodedData < 3 ) // out of space? go to final write logic
+ break;
+ if ( pubDecodedData )
+ {
+ pubDecodedData[0] = (uint8)( un24BitsWithSentinel >> 16 );
+ pubDecodedData[1] = (uint8)( un24BitsWithSentinel >> 8);
+ pubDecodedData[2] = (uint8)( un24BitsWithSentinel );
+ pubDecodedData += 3;
+ }
+ cubDecodedData -= 3;
+ un24BitsWithSentinel = 1;
+ }
+ }
+
+ // If un24BitsWithSentinel contains data, output the remaining full bytes
+ if ( un24BitsWithSentinel >= (1<<6) )
+ {
+ // Possibilities are 3, 2, 1, or 0 full output bytes.
+ int nWriteBytes = 3;
+ while ( un24BitsWithSentinel < (1<<24) )
+ {
+ nWriteBytes--;
+ un24BitsWithSentinel <<= 6;
+ }
+
+ // Write completed bytes to output
+ while ( nWriteBytes-- > 0 )
+ {
+ if ( cubDecodedData == 0 )
+ {
+ AssertMsg( false, "CCrypto::Base64Decode: insufficient output buffer (up to n*3/4+2 bytes required)" );
+ goto decode_failed;
+ }
+ if ( pubDecodedData )
+ {
+ *pubDecodedData++ = (uint8)(un24BitsWithSentinel >> 16);
+ }
+ --cubDecodedData;
+ un24BitsWithSentinel <<= 8;
+ }
+ }
+
+ *pcubDecodedData = cubDecodedDataOrig - cubDecodedData;
+ return true;
+
+decode_failed:
+ *pcubDecodedData = cubDecodedDataOrig - cubDecodedData;
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a SHA1 hash
+// Input: pchInput - Plaintext string of item to hash (null terminated)
+// pOutDigest - Pointer to receive hashed digest output
+//-----------------------------------------------------------------------------
+bool CCrypto::GenerateSHA1Digest( const uint8 *pubInput, const int cubInput, SHADigest_t *pOutDigest )
+{
+ VPROF_BUDGET( "CCrypto::GenerateSHA1Digest", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubInput );
+ Assert( cubInput > 0 );
+ Assert( pOutDigest );
+
+ bool bSuccess = true;
+ try
+ {
+ CryptoPP::SHA().CalculateDigest( *pOutDigest, pubInput, cubInput );
+ }
+ catch(...)
+ {
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a hash Salt - be careful, over-writing an existing salt
+// will render the hashed value unverifiable.
+//-----------------------------------------------------------------------------
+bool CCrypto::GenerateSalt( Salt_t *pSalt )
+{
+ VPROF_BUDGET( "CCrypto::GenerateSalt", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pSalt );
+
+ bool bSuccess = true;
+ try
+ {
+ CPoolAllocatedRNG rng;
+ rng.GetRNG().GenerateBlock( (byte*)pSalt, sizeof(Salt_t) );
+ }
+ catch(...)
+ {
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a SHA1 hash using a salt.
+// Input: pchInput - Plaintext string of item to hash (null terminated)
+// pSalt - Salt
+// pOutDigest - Pointer to receive salted digest output
+//-----------------------------------------------------------------------------
+bool CCrypto::GenerateSaltedSHA1Digest( const char *pchInput, const Salt_t *pSalt, SHADigest_t *pOutDigest )
+{
+ VPROF_BUDGET( "CCrypto::GenerateSaltedSHA1Digest", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pchInput );
+ Assert( pSalt );
+ Assert( pOutDigest );
+
+ int iInputLen = Q_strlen( pchInput );
+ uint8 *pubSaltedInput = new uint8[ iInputLen + sizeof( Salt_t ) ];
+
+ // Insert half the salt before the input string and half at the end.
+ // This is probably unnecessary (to split the salt) but we're stuck with it for historical reasons.
+ uint8 *pubCursor = pubSaltedInput;
+ Q_memcpy( pubCursor, (uint8 *)pSalt, sizeof(Salt_t) / 2 );
+ pubCursor += sizeof( Salt_t ) / 2;
+ Q_memcpy( pubCursor, pchInput, iInputLen );
+ pubCursor += iInputLen;
+ Q_memcpy( pubCursor, (uint8 *)pSalt + sizeof(Salt_t) / 2, sizeof(Salt_t) / 2 );
+
+ bool bSuccess = true;
+ try
+ {
+ CryptoPP::SHA().CalculateDigest( *pOutDigest, pubSaltedInput, iInputLen + sizeof( Salt_t ) );
+ }
+ catch(...)
+ {
+ bSuccess = false;
+ }
+
+ delete [] pubSaltedInput;
+
+ return bSuccess;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates a random block of data
+//-----------------------------------------------------------------------------
+bool CCrypto::GenerateRandomBlock( uint8 *pubDest, int cubDest )
+{
+ CPoolAllocatedRNG rng;
+ rng.GetRNG().GenerateBlock( pubDest, cubDest );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a keyed-hash MAC using SHA1
+// Input: pubData - Plaintext data to digest
+// cubData - length of data
+// pubKey - key to use in HMAC
+// cubKey - length of key
+// pOutDigest - Pointer to receive hashed digest output
+//-----------------------------------------------------------------------------
+bool CCrypto::GenerateHMAC( const uint8 *pubData, uint32 cubData, const uint8 *pubKey, uint32 cubKey, SHADigest_t *pOutputDigest )
+{
+ VPROF_BUDGET( "CCrypto::GenerateHMAC", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( cubData > 0 );
+ Assert( pubKey );
+ Assert( cubKey > 0 );
+ Assert( pOutputDigest );
+
+ bool bSuccess = true;
+ try
+ {
+ CryptoPP::HMAC< CryptoPP::SHA1 > hmac( pubKey, cubKey );
+ hmac.CalculateDigest( *pOutputDigest, pubData, cubData );
+ }
+ catch(...)
+ {
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a keyed-hash MAC using SHA-256
+//-----------------------------------------------------------------------------
+bool CCrypto::GenerateHMAC256( const uint8 *pubData, uint32 cubData, const uint8 *pubKey, uint32 cubKey, SHA256Digest_t *pOutputDigest )
+{
+ VPROF_BUDGET( "CCrypto::GenerateHMAC256", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( cubData > 0 );
+ Assert( pubKey );
+ Assert( cubKey > 0 );
+ Assert( pOutputDigest );
+
+ bool bSuccess = true;
+ try
+ {
+ CryptoPP::HMAC< CryptoPP::SHA256 > hmac( pubKey, cubKey );
+ hmac.CalculateDigest( *pOutputDigest, pubData, cubData );
+ }
+ catch(...)
+ {
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+
+bool CCrypto::BGzipBuffer( const uint8 *pubData, uint32 cubData, CCryptoOutBuffer &bufOutput )
+{
+ bool bSuccess = true;
+ try
+ {
+ std::string gzip_output;
+ StringSource( (byte *)pubData, cubData, true, new Gzip( new StringSink( gzip_output ) ) );
+ bufOutput.Set( (uint8*)gzip_output.c_str(), (uint32)gzip_output.length() );
+ }
+ catch( ... )
+ {
+ bSuccess = false;
+ }
+ return bSuccess;
+}
+
+
+bool CCrypto::BGunzipBuffer( const uint8 *pubData, uint32 cubData, CCryptoOutBuffer &bufOutput )
+{
+ bool bSuccess = true;
+ try
+ {
+ std::string gunzip_output;
+ StringSource( (byte *)pubData, cubData, true, new Gunzip( new StringSink( gunzip_output ) ) );
+ bufOutput.Set( (uint8*)gunzip_output.c_str(), (uint32)gunzip_output.length() );
+ }
+ catch( ... )
+ {
+ bSuccess = false;
+ }
+ return bSuccess;
+}
+
+
+//! These are all needed to get around stack-overflow bug in Initialize()
+class HexDecoderTKS : public HexDecoder
+{
+public:
+ HexDecoderTKS(BufferedTransformation *attachment, const int *pnDecodingArray)
+ : HexDecoder(attachment)
+ {
+ BaseN_Decoder::IsolatedInitialize( MakeParameters( Name::DecodingLookupArray(), pnDecodingArray )( Name::Log2Base(), 4 ) );
+ }
+};
+
+class Base32DecoderTKS : public Base32Decoder
+{
+public:
+ Base32DecoderTKS(BufferedTransformation *attachment, const int *pnDecodingArray)
+ : Base32Decoder(attachment)
+ {
+ BaseN_Decoder::IsolatedInitialize( MakeParameters( Name::DecodingLookupArray(), pnDecodingArray )( Name::Log2Base(), 5 ) );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Implement hex encoding / decoding using a custom lookup table.
+// This is a class because the decoding is done via a generated
+// reverse-lookup table, and to save time it's best to just create
+// that table once.
+//-----------------------------------------------------------------------------
+CCustomHexEncoder::CCustomHexEncoder( const char *pchEncodingTable )
+{
+ m_bValidEncoding = false;
+ if ( Q_strlen( pchEncodingTable ) == sizeof( m_rgubEncodingTable ) )
+ {
+ Q_memcpy( m_rgubEncodingTable, pchEncodingTable, sizeof( m_rgubEncodingTable ) );
+
+ BaseN_Decoder::InitializeDecodingLookupArray( m_rgnDecodingTable, m_rgubEncodingTable, 16, false );
+ m_bValidEncoding = true;
+ }
+ else
+ {
+ AssertMsg( false, "CCrypto::CustomHexEncoder: Improper encoding table\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CCustomHexEncoder::~CCustomHexEncoder()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hex-encodes a block of data. (Binary -> text representation.) The output
+// is null-terminated and can be treated as a string.
+// Uses the supplied custom encoding characters
+// Input: pubData - Data to encode
+// cubData - Size of data to encode
+// pchEncodedData - Pointer to string buffer to store output in
+// cchEncodedData - Size of pchEncodedData buffer
+//-----------------------------------------------------------------------------
+bool CCustomHexEncoder::Encode( const uint8 *pubData, const uint32 cubData, char *pchEncodedData, uint32 cchEncodedData )
+{
+ VPROF_BUDGET( "CCrypto::CustomHexEncode", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( cubData );
+ Assert( pchEncodedData );
+ Assert( cchEncodedData > 0 );
+
+ if ( !m_bValidEncoding )
+ return false;
+
+ ArraySink * pArraySinkOutput = new ArraySink( (byte *) pchEncodedData, cchEncodedData );
+ // Note: HexEncoder now owns the pointer to pOutputSink and will free it when the encoder goes out of scope and destructs
+ HexEncoder hexEncoder( pArraySinkOutput );
+ hexEncoder.IsolatedInitialize( MakeParameters( Name::EncodingLookupArray(), (const uint8 *)m_rgubEncodingTable ) );
+ hexEncoder.Put( pubData, cubData );
+ hexEncoder.MessageEnd();
+
+ uint32 len = pArraySinkOutput->TotalPutLength();
+ pchEncodedData[len] = 0; // NULL-terminate
+ if ( len >= cchEncodedData )
+ {
+ AssertMsg2( false, "CCrypto::CustomHexEncode: insufficient output buffer for encoding, needed %d got %d\n",
+ len, cchEncodedData );
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hex-decodes a block of data. (Text -> binary representation.)
+// With custom encoding-table
+// Input: pchData - Null-terminated hex-encoded string
+// pubDecodedData - Pointer to buffer to store output in
+// pcubDecodedData - Pointer to variable that contains size of
+// output buffer. At exit, is filled in with actual size
+// of decoded data.
+//-----------------------------------------------------------------------------
+bool CCustomHexEncoder::Decode( const char *pchData, uint8 *pubDecodedData, uint32 *pcubDecodedData )
+{
+ VPROF_BUDGET( "CCrypto::CustomHexDecode", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pchData );
+ Assert( pubDecodedData );
+ Assert( pcubDecodedData );
+ Assert( *pcubDecodedData );
+
+ if ( !m_bValidEncoding )
+ return false;
+
+ ArraySink * pArraySinkOutput = new ArraySink( pubDecodedData, *pcubDecodedData );
+ // Note: HexEncoder now owns the pointer to pOutputSink and will free it when the encoder goes out of scope and destructs
+ HexDecoderTKS hexDecoder( pArraySinkOutput, (const int *)m_rgnDecodingTable );
+ hexDecoder.Put( (byte *) pchData, Q_strlen( pchData ) );
+ hexDecoder.MessageEnd();
+
+ uint32 len = pArraySinkOutput->TotalPutLength();
+ if ( len > *pcubDecodedData )
+ {
+ AssertMsg2( false, "CCrypto::CustomHexDecode: insufficient output buffer for decoding, needed %d got %d\n",
+ len, *pcubDecodedData );
+ return false;
+ }
+ *pcubDecodedData = len;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Implement hex encoding / decoding using a custom lookup table.
+// This is a class because the decoding is done via a generated
+// reverse-lookup table, and to save time it's best to just create
+// that table once.
+//-----------------------------------------------------------------------------
+CCustomBase32Encoder::CCustomBase32Encoder( const char *pchEncodingTable )
+{
+ m_bValidEncoding = false;
+ if ( Q_strlen( pchEncodingTable ) == sizeof( m_rgubEncodingTable ) )
+ {
+ Q_memcpy( m_rgubEncodingTable, pchEncodingTable, sizeof( m_rgubEncodingTable ) );
+
+ BaseN_Decoder::InitializeDecodingLookupArray( m_rgnDecodingTable, m_rgubEncodingTable, 32, false );
+ m_bValidEncoding = true;
+ }
+ else
+ {
+ AssertMsg( false, "CCrypto::CustomBase32Encoder: Improper encoding table\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CCustomBase32Encoder::~CCustomBase32Encoder()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base32-encodes a block of data. (Binary -> text representation.) The output
+// is null-terminated and can be treated as a string.
+// Uses the supplied custom encoding table
+// Input: pubData - Data to encode
+// cubData - Size of data to encode
+// pchEncodedData - Pointer to string buffer to store output in
+// cchEncodedData - Size of pchEncodedData buffer
+//-----------------------------------------------------------------------------
+bool CCustomBase32Encoder::Encode( const uint8 *pubData, const uint32 cubData, char *pchEncodedData, uint32 cchEncodedData )
+{
+ VPROF_BUDGET( "CCrypto::CustomBase32Encode", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pubData );
+ Assert( cubData );
+ Assert( pchEncodedData );
+ Assert( cchEncodedData > 0 );
+
+ if ( !m_bValidEncoding )
+ return false;
+
+ ArraySink * pArraySinkOutput = new ArraySink( (byte *) pchEncodedData, cchEncodedData );
+ // Note: Base32Encoder now owns the pointer to pOutputSink and will free it when the encoder goes out of scope and destructs
+ Base32Encoder base32Encoder( pArraySinkOutput );
+ base32Encoder.IsolatedInitialize( MakeParameters( Name::EncodingLookupArray(), (const uint8 *)m_rgubEncodingTable ) );
+ base32Encoder.Put( pubData, cubData );
+ base32Encoder.MessageEnd();
+
+ uint32 len = pArraySinkOutput->TotalPutLength();
+ pchEncodedData[len] = 0; // NULL-terminate
+ if ( len >= cchEncodedData )
+ {
+ AssertMsg2( false, "CCrypto::CustomBase32Encode: insufficient output buffer for encoding, needed %d got %d\n",
+ len, cchEncodedData );
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base32-decodes a block of data. (Text -> binary representation.)
+// With custom encoding table
+// Input: pchData - Null-terminated hex-encoded string
+// pubDecodedData - Pointer to buffer to store output in
+// pcubDecodedData - Pointer to variable that contains size of
+// output buffer. At exit, is filled in with actual size
+// of decoded data.
+//-----------------------------------------------------------------------------
+bool CCustomBase32Encoder::Decode( const char *pchData, uint8 *pubDecodedData, uint32 *pcubDecodedData )
+{
+ VPROF_BUDGET( "CCrypto::CustomBase32Decode", VPROF_BUDGETGROUP_ENCRYPTION );
+ Assert( pchData );
+ Assert( pubDecodedData );
+ Assert( pcubDecodedData );
+ Assert( *pcubDecodedData );
+
+ if ( !m_bValidEncoding )
+ return false;
+
+ ArraySink * pArraySinkOutput = new ArraySink( pubDecodedData, *pcubDecodedData );
+ // Note: Base32Encoder now owns the pointer to pOutputSink and will free it when the encoder goes out of scope and destructs
+ Base32DecoderTKS base32Decoder( pArraySinkOutput, (const int *)m_rgnDecodingTable );
+ base32Decoder.Put( (byte *) pchData, Q_strlen( pchData ) );
+ base32Decoder.MessageEnd();
+
+ uint32 len = pArraySinkOutput->TotalPutLength();
+ if ( len > *pcubDecodedData )
+ {
+ AssertMsg2( false, "CCrypto::CustomBase32Decode: insufficient output buffer for decoding, needed %d got %d\n",
+ len, *pcubDecodedData );
+ return false;
+ }
+ *pcubDecodedData = len;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base32-encodes a block of data. (Binary -> text representation.) The output
+// is null-terminated and can be treated as a string.
+// Uses the supplied custom encoding table, and a bit-stream input source
+// (not necessarily an integer number of bytes).
+// Input: pBitStringData - Data to encode
+// pchEncodedData - Pointer to string buffer to store output in
+// cchEncodedData - Size of pchEncodedData buffer
+//-----------------------------------------------------------------------------
+bool CCustomBase32Encoder::Encode( CSimpleBitString *pBitStringData, char *pchEncodedData, uint32 cchEncodedData )
+{
+ // This is useful if you have, say, 125 bits of information and
+ // want to encode them into 25 base32-encoded characters.
+ uint32 cBits = pBitStringData->GetCurrNumBits();
+
+ uint32 cCharacters = (cBits / 5);
+ uint32 cBitsRemainder = cBits % 5;
+ if ( cBitsRemainder )
+ cCharacters++;
+
+ // GTE because of NULL
+ if ( cCharacters >= cchEncodedData )
+ return false;
+
+ CSimpleBitString::iterator itBitString( *pBitStringData );
+ uint ich = 0;
+ for ( ; ich < cCharacters; ++ich )
+ {
+ uint32 unCodon = itBitString.GetNextBits( 5 );
+ // Pad w/ zero bits to integer num codons
+ if ( ich == (cCharacters - 1) )
+ unCodon <<= cBitsRemainder;
+
+ pchEncodedData[ich] = m_rgubEncodingTable[ unCodon ];
+ }
+
+ // NULL
+ pchEncodedData[ich] = 0;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base32-decodes a block of data. (Text -> binary representation.)
+// With custom encoding table, and a BitString output
+// Input: pchData - Null-terminated base32-encoded string
+// pBitStringDecodedData - Pointer to BitString to receive decoded data
+//-----------------------------------------------------------------------------
+bool CCustomBase32Encoder::Decode( const char *pchData, CSimpleBitString *pBitStringDecodedData )
+{
+ // Note: 25 base32-encoded characters contain 125 bits of information.
+ // Decoded into a byte buffer, this yields 15 bytes plus 5 bits of padding.
+ // Decoded into a CSimpleBitString, it will yield all 125 bits
+ while ( *pchData )
+ {
+ uint32 unData = m_rgnDecodingTable[(unsigned)*pchData++];
+ if ( unData == 0xFFFFFFFF )
+ return false;
+
+ pBitStringDecodedData->AppendBits( unData, 5 );
+ }
+
+ return true;
+}
+
+#ifdef DBGFLAG_VALIDATE
+//-----------------------------------------------------------------------------
+// Purpose: validates memory structures
+//-----------------------------------------------------------------------------
+void CCrypto::ValidateStatics( CValidator &validator, const char *pchName )
+{
+ ValidateObj( g_tslistPAutoSeededRNG );
+}
+#endif // DBGFLAG_VALIDATE
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a plaintext password, check whether it matches an existing
+// hash
+//-----------------------------------------------------------------------------
+bool CCrypto::BValidatePasswordHash( const char *pchInput, EPasswordHashAlg hashType, const PasswordHash_t &DigestStored, const Salt_t &Salt, PasswordHash_t *pDigestComputed )
+{
+ VPROF_BUDGET( "CCrypto::BValidatePasswordHash", VPROF_BUDGETGROUP_ENCRYPTION );
+
+ bool bResult = false;
+ size_t cDigest = k_HashLengths[hashType];
+ Assert( cDigest != 0 );
+ PasswordHash_t tmpDigest;
+ PasswordHash_t *pOutputDigest = pDigestComputed;
+ if ( pOutputDigest == NULL )
+ {
+ pOutputDigest = &tmpDigest;
+ }
+
+ BGeneratePasswordHash( pchInput, hashType, Salt, *pOutputDigest );
+ bResult = ( 0 == Q_memcmp( &DigestStored, pOutputDigest, cDigest ) );
+
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a plaintext password and salt, generate a password hash of
+// the requested type.
+//-----------------------------------------------------------------------------
+bool CCrypto::BGeneratePasswordHash( const char *pchInput, EPasswordHashAlg hashType, const Salt_t &Salt, PasswordHash_t &OutPasswordHash )
+{
+ VPROF_BUDGET( "CCrypto::BGeneratePasswordHash", VPROF_BUDGETGROUP_ENCRYPTION );
+
+ bool bResult = false;
+ size_t cDigest = k_HashLengths[hashType];
+
+ switch ( hashType )
+ {
+ case k_EHashSHA1:
+ bResult = CCrypto::GenerateSaltedSHA1Digest( pchInput, &Salt, (SHADigest_t *)&OutPasswordHash.sha );
+ break;
+ case k_EHashBigPassword:
+ {
+ //
+ // This is a fake algorithm to test widening of the column. It's a salted SHA-1 hash with 0x01 padding
+ // on either side of it.
+ //
+ size_t cDigestSHA1 = k_HashLengths[k_EHashSHA1];
+ size_t cPadding = ( cDigest - cDigestSHA1 ) / 2;
+
+ AssertMsg( ( ( cDigest - cDigestSHA1 ) % 2 ) == 0, "Invalid hash width for k_EHashBigPassword, needs to be even." );
+
+ CCrypto::GenerateSaltedSHA1Digest( pchInput, &Salt, (SHADigest_t *)( (uint8 *)&OutPasswordHash.bigpassword + cPadding ) );
+ Q_memset( (uint8 *)&OutPasswordHash, 0x01, cPadding );
+ Q_memset( (uint8 *)&OutPasswordHash + cPadding + cDigestSHA1 , 0x01, cPadding );
+ bResult = true;
+ break;
+ }
+ case k_EHashPBKDF2_1000:
+ bResult = CCrypto::BGeneratePBKDF2Hash( pchInput, Salt, 1000, OutPasswordHash );
+ break;
+ case k_EHashPBKDF2_5000:
+ bResult = CCrypto::BGeneratePBKDF2Hash( pchInput, Salt, 5000, OutPasswordHash );
+ break;
+ case k_EHashPBKDF2_10000:
+ bResult = CCrypto::BGeneratePBKDF2Hash( pchInput, Salt, 10000, OutPasswordHash );
+ break;
+ case k_EHashSHA1WrappedWithPBKDF2_10000:
+ bResult = CCrypto::BGenerateWrappedSHA1PasswordHash( pchInput, Salt, 10000, OutPasswordHash );
+ break;
+ default:
+ AssertMsg1( false, "Invalid password hash type %u passed to BGeneratePasswordHash\n", hashType );
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a plaintext password and salt and a count of rounds, generate a PBKDF2 hash
+// with the requested number of rounds.
+//-----------------------------------------------------------------------------
+bool CCrypto::BGeneratePBKDF2Hash( const char* pchInput, const Salt_t &Salt, unsigned int rounds, PasswordHash_t &OutPasswordHash )
+{
+ PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
+
+ unsigned int iterations = pbkdf.DeriveKey( (byte *)&OutPasswordHash.pbkdf2, sizeof(OutPasswordHash.pbkdf2), 0, (const byte *)pchInput, Q_strlen(pchInput), (const byte *)&Salt, sizeof(Salt), rounds );
+
+ return ( iterations == rounds );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a plaintext password and salt and a count of rounds, generate a SHA1 hash wrapped with
+// a PBKDF2 hash with the specified number of rounds.
+// Used to provide a stronger password hash for accounts that haven't logged in in a while.
+//-----------------------------------------------------------------------------
+bool CCrypto::BGenerateWrappedSHA1PasswordHash( const char *pchInput, const Salt_t &Salt, unsigned int rounds, PasswordHash_t &OutPasswordHash )
+{
+ bool bResult;
+ bResult = CCrypto::GenerateSaltedSHA1Digest( pchInput, &Salt, (SHADigest_t *)&OutPasswordHash.sha );
+ if ( bResult )
+ {
+ PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
+ unsigned int iterations = pbkdf.DeriveKey( (byte *)&OutPasswordHash.pbkdf2, sizeof(OutPasswordHash.pbkdf2), 0, (const byte *)&OutPasswordHash.sha, sizeof(OutPasswordHash.sha), (const byte *)&Salt, sizeof(Salt), rounds );
+
+ bResult = ( iterations == rounds );
+ }
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given an existing password hash and salt, attempt to construct a stronger
+// password hash and return the new hash type.
+//
+// Currently the only transformation available is from a SHA1 (or BigPassword)
+// hash to a PBKDF2 hash with 10,000 rounds. In the future this function
+// may be extended to allow additional transformations.
+//-----------------------------------------------------------------------------
+bool CCrypto::BUpgradeOrWrapPasswordHash( PasswordHash_t &InPasswordHash, EPasswordHashAlg hashTypeIn, const Salt_t &Salt, PasswordHash_t &OutPasswordHash, EPasswordHashAlg &hashTypeOut )
+{
+ bool bResult = true;;
+
+ if ( hashTypeIn == k_EHashSHA1 || hashTypeIn == k_EHashBigPassword )
+ {
+ //
+ // Can wrap a SHA1 hash with any PBKDF variant, but right now only 10,000 rounds is
+ // implemented.
+ //
+ if ( hashTypeOut == k_EHashPBKDF2_10000 )
+ {
+ hashTypeOut = k_EHashSHA1WrappedWithPBKDF2_10000;
+
+ byte * pbHash;
+ if ( hashTypeIn == k_EHashSHA1 )
+ {
+ pbHash = (byte *)&InPasswordHash.sha;
+ }
+ else
+ {
+ //
+ // Need to unroll BigPasswordHash into unpadded SHA1
+ //
+ size_t cDigest = k_HashLengths[k_EHashBigPassword];
+ size_t cDigestSHA1 = k_HashLengths[k_EHashSHA1];
+ size_t cPadding = ( cDigest - cDigestSHA1 ) / 2;
+
+ AssertMsg( ( ( cDigest - cDigestSHA1 ) % 2 ) == 0, "Invalid hash width for k_EHashBigPassword, needs to be even." );
+ pbHash = (byte *)&InPasswordHash.sha + cPadding;
+ }
+
+ PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
+ PasswordHash_t passOut;
+ unsigned int iterations = pbkdf.DeriveKey( (byte *)passOut.pbkdf2, sizeof(passOut.pbkdf2), 0, pbHash, k_HashLengths[k_EHashSHA1], (const byte *)&Salt, sizeof(Salt), 10000 );
+
+ bResult = ( iterations == 10000 );
+ if ( bResult )
+ {
+ Q_memcpy( &OutPasswordHash, &passOut, sizeof(OutPasswordHash) );
+ }
+ }
+ else
+ {
+ Assert( hashTypeOut == k_EHashPBKDF2_10000 );
+ bResult = false;
+ }
+ }
+ else
+ {
+ bResult = false;
+ Assert( false );
+ }
+
+ return bResult;
+}
+