#include <mutex>
#include <thread>
#include <tier0/dbg.h>
#include "opensslwrapper.h"
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "crypto.h"
int COpenSSLWrapper::m_nInstances;
static std::recursive_mutex *s_pMutexArray;
int COpenSSLWrapper::s_nContextDataIndex;
int COpenSSLWrapper::s_nConnectionDataIndex;
#ifdef _DEBUG
int COpenSSLWrapper::m_nBytesLeaked = 0;
#endif
struct CRYPTO_dynlock_value
{
std::recursive_mutex m_Mutex;
};
#if !IsAndroid() && ( OPENSSL_VERSION_NUMBER < 0x30000000 )
#define OPENSSL_CUSTOM_RAND
static int RAND_CryptoGenRandom_bytes( unsigned char *buf, int num ) {
CCrypto::GenerateRandomBlock( buf, num );
return 1;
}
static int RAND_CryptoGenRandom_status() { return 1; }
static const RAND_METHOD RAND_CryptoGenRandom =
{
NULL, RAND_CryptoGenRandom_bytes, NULL, NULL, RAND_CryptoGenRandom_bytes, RAND_CryptoGenRandom_status };
#endif
void COpenSSLWrapper::Initialize()
{
int iStatus;
if ( m_nInstances++ == 0 )
{
s_pMutexArray = new std::recursive_mutex[CRYPTO_num_locks()];
CRYPTO_set_locking_callback( COpenSSLWrapper::OpenSSLLockingCallback );
CRYPTO_set_id_callback( COpenSSLWrapper::OpenSSLThreadIDCallback );
CRYPTO_set_dynlock_create_callback( COpenSSLWrapper::OpenSSLDynLockCreateCallback );
CRYPTO_set_dynlock_destroy_callback( COpenSSLWrapper::OpenSSLDynLockDestroyCallback );
CRYPTO_set_dynlock_lock_callback( COpenSSLWrapper::OpenSSLDynLockLockCallback );
#ifdef OPENSSL_CUSTOM_RAND
RAND_set_rand_method( &RAND_CryptoGenRandom );
#endif
iStatus = RAND_status();
AssertMsg( iStatus == 1, "OpenSSL random number system reports not enough entropy" );
}
}
void COpenSSLWrapper::Shutdown()
{
if ( m_nInstances-- == 1 )
{
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
CRYPTO_set_locking_callback( NULL );
CRYPTO_set_id_callback( NULL );
CRYPTO_set_dynlock_create_callback( NULL );
CRYPTO_set_dynlock_destroy_callback( NULL );
CRYPTO_set_dynlock_lock_callback( NULL );
delete[] s_pMutexArray;
s_pMutexArray = NULL;
}
}
void COpenSSLWrapper::OpenSSLLockingCallback( int mode, int type, const char *file, int line )
{
if ( s_pMutexArray == NULL )
return;
if ( mode & CRYPTO_LOCK )
{
s_pMutexArray[type].lock();
}
else
{
s_pMutexArray[type].unlock();
}
}
unsigned long COpenSSLWrapper::OpenSSLThreadIDCallback( void )
{
std::hash<std::thread::id> hash;
return (unsigned long)hash( std::this_thread::get_id() );
}
CRYPTO_dynlock_value* COpenSSLWrapper::OpenSSLDynLockCreateCallback( const char* file, int line )
{
return new CRYPTO_dynlock_value;
}
void COpenSSLWrapper::OpenSSLDynLockDestroyCallback( CRYPTO_dynlock_value * l, const char *file, int line )
{
delete l;
}
void COpenSSLWrapper::OpenSSLDynLockLockCallback( int mode, CRYPTO_dynlock_value *l, const char* file, int line )
{
if ( mode & CRYPTO_LOCK )
{
l->m_Mutex.lock();
}
else
{
l->m_Mutex.unlock();
}
}
#ifdef _DEBUG
void *COpenSSLWrapper::OpenSSLMemLeakCallback( unsigned long order, const char *file, int line, int num_bytes, void * addr )
{
m_nBytesLeaked += num_bytes;
return addr;
}
#endif
#ifdef DBGFLAG_VALIDATE
void COpenSSLWrapper::ValidateStatics( CValidator &validator, const char *pchName )
{
if ( COpenSSLWrapper::s_pMutexArray && !validator.IsClaimed( COpenSSLWrapper::s_pMutexArray ) )
{
validator.ClaimArrayMemory( COpenSSLWrapper::s_pMutexArray );
for( int i=0; i < CRYPTO_num_locks(); ++i )
{
validator.ClaimMemory( s_pMutexArray[i] );
}
}
}
#endif