Skip to content
Open
6 changes: 0 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,6 @@ if(USE_CRYPTO STREQUAL "libsodium" OR USE_CRYPTO25519 STREQUAL "libsodium")
find_package(sodium REQUIRED)
endif()

if(USE_CRYPTO STREQUAL "libsodium")
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*|i686.*|i386.*|x86.*")
message(FATAL_ERROR "-DUSE_CRYPTO=libsodium invalid, libsodium AES implementation only works on x86/x86_64 CPUs")
endif()
endif()

# We always need at least sse2 on x86
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*|i686.*|i386.*|x86.*")
set(TARGET_ARCH_FLAGS "-msse2")
Expand Down
57 changes: 56 additions & 1 deletion src/common/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class SymmetricCryptContextBase
void Wipe();

protected:
void *m_ctx;
void *m_ctx = nullptr;

uint32 m_cbIV, m_cbTag;
};
Expand Down Expand Up @@ -68,6 +68,9 @@ class AES_GCM_CipherContext : public SymmetricCryptContextBase

// Initialize context with the specified private key, IV size, and tag size
bool InitCipher( const void *pKey, size_t cbKey, size_t cbIV, size_t cbTag, bool bEncrypt );

// Determine whether AES_GCM is supported on this system + crypto backend
static bool IsAvailable();
};

/// Encryption context for AES-GCM
Expand Down Expand Up @@ -110,6 +113,58 @@ class AES_GCM_DecryptContext final : public AES_GCM_CipherContext, public ISymme
) override;
};

/// Base class for ChaCha20-Poly1305 encryption and decryption
class ChaCha20_Poly1305_CipherContext : public SymmetricCryptContextBase
{
public:

// Initialize context with the specified private key, IV size, and tag size
bool InitCipher(const void* pKey, size_t cbKey, size_t cbIV, size_t cbTag, bool bEncrypt);

// Determine whether ChaCha20-Poly1305 is supported on this system + crypto backend
static bool IsAvailable();
};

/// Encryption context for ChaCha20-Poly1305
class ChaCha20_Poly1305_EncryptContext final : public ChaCha20_Poly1305_CipherContext, public ISymmetricEncryptContext
{
public:

// Initialize context with the specified private key, IV size, and tag size
inline bool Init(const void* pKey, size_t cbKey, size_t cbIV, size_t cbTag)
{
return InitCipher(pKey, cbKey, cbIV, cbTag, true);
}

// Implements ISymmetricEncryptContext
virtual bool Encrypt(
const void* pPlaintextData, size_t cbPlaintextData,
const void* pIV,
void* pEncryptedDataAndTag, uint32* pcbEncryptedDataAndTag,
const void* pAdditionalAuthenticationData, size_t cbAuthenticationData // Optional additional authentication data. Not encrypted, but will be included in the tag, so it can be authenticated.
) override;
};

/// Decryption context for ChaCha20-Poly1305
class ChaCha20_Poly1305_DecryptContext final : public ChaCha20_Poly1305_CipherContext, public ISymmetricDecryptContext
{
public:

// Initialize context with the specified private key, IV size, and tag size
inline bool Init(const void* pKey, size_t cbKey, size_t cbIV, size_t cbTag)
{
return InitCipher(pKey, cbKey, cbIV, cbTag, false);
}

// Implements ISymmetricDecryptContext
virtual bool Decrypt(
const void* pEncryptedDataAndTag, size_t cbEncryptedDataAndTag,
const void* pIV,
void* pPlaintextData, uint32* pcbPlaintextData,
const void* pAdditionalAuthenticationData, size_t cbAuthenticationData // Optional additional authentication data. Not encrypted, but will be included in the tag, so it can be authenticated.
) override;
};

namespace CCrypto
{
void Init();
Expand Down
43 changes: 43 additions & 0 deletions src/common/crypto_bcrypt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef struct _BCryptContext {
ULONG cbKeyObject;

_BCryptContext() {
hAlgAES = INVALID_HANDLE_VALUE;
hKey = INVALID_HANDLE_VALUE;
pbKeyObject = NULL;
cbKeyObject = 0;
Expand Down Expand Up @@ -150,6 +151,15 @@ bool AES_GCM_CipherContext::InitCipher( const void *pKey, size_t cbKey, size_t c
return true;
}

bool AES_GCM_CipherContext::IsAvailable()
{
BCryptContext ctx;
if (BCryptOpenAlgorithmProvider(&ctx.hAlgAES, BCRYPT_AES_ALGORITHM, nullptr, 0) != 0)
return false;
AssertFatal(ctx.hAlgAES != INVALID_HANDLE_VALUE);
return true;
}

bool AES_GCM_EncryptContext::Encrypt(
const void *pPlaintextData, size_t cbPlaintextData,
const void *pIV,
Expand Down Expand Up @@ -218,6 +228,39 @@ bool AES_GCM_DecryptContext::Decrypt(
return NT_SUCCESS(status);
}

bool ChaCha20_Poly1305_CipherContext::InitCipher(const void* /*pKey*/, size_t /*cbKey*/, size_t /*cbIV*/, size_t /*cbTag*/, bool /*bEncrypt*/)
{
AssertMsg(false, "The ChaCha20-Poly1305 algorithm is not implemented in the BCrypt library");
return false;
}

bool ChaCha20_Poly1305_CipherContext::IsAvailable()
{
return false;
}

bool ChaCha20_Poly1305_EncryptContext::Encrypt(
const void* /*pPlaintextData*/, size_t /*cbPlaintextData*/,
const void* /*pIV*/,
void* /*pEncryptedDataAndTag*/, uint32* /*pcbEncryptedDataAndTag*/,
const void* /*pAdditionalAuthenticationData*/, size_t /*cbAuthenticationData*/
)
{
AssertMsg(false, "The ChaCha20-Poly1305 algorithm is not implemented in the BCrypt library");
return false;
}

bool AES_GCM_DecryptContext::Decrypt(
const void* /*pEncryptedDataAndTag*/, size_t /*cbEncryptedDataAndTag*/,
const void* /*pIV*/,
void* /*pPlaintextData*/, uint32* /*pcbPlaintextData*/,
const void* /*pAdditionalAuthenticationData*/, size_t /*cbAuthenticationData*/
)
{
AssertMsg(false, "The ChaCha20-Poly1305 algorithm is not implemented in the BCrypt library");
return false;
}

//-----------------------------------------------------------------------------
// Purpose: Generate a SHA256 hash
// Input: pchInput - Plaintext string of item to hash (null terminated)
Expand Down
118 changes: 115 additions & 3 deletions src/common/crypto_libsodium.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
#include "crypto.h"

#ifdef VALVE_CRYPTO_LIBSODIUM

#include <tier0/vprof.h>
#include <tier0/dbg.h>

#include "tier0/memdbgoff.h"
#include <sodium/core.h>
#include <sodium/crypto_aead_aes256gcm.h>
#include <sodium/crypto_aead_chacha20poly1305.h>
#include <sodium/crypto_auth_hmacsha256.h>
#include <sodium/crypto_hash_sha256.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include "tier0/memdbgon.h"

#ifdef VALVE_CRYPTO_LIBSODIUM

SymmetricCryptContextBase::SymmetricCryptContextBase()
: m_ctx(nullptr), m_cbIV(0), m_cbTag(0)
{
Expand All @@ -37,7 +38,7 @@ bool AES_GCM_CipherContext::InitCipher( const void *pKey, size_t cbKey, size_t c
// November 2019 survey.
// Libsodium recommends ChaCha20-Poly1305 in software if you've not got AES support
// in hardware.
if ( crypto_aead_aes256gcm_is_available() != 1 )
if ( !IsAvailable() )
{
AssertMsg( false, "No hardware AES support on this CPU." );
return false;
Expand Down Expand Up @@ -70,6 +71,16 @@ bool AES_GCM_CipherContext::InitCipher( const void *pKey, size_t cbKey, size_t c
return true;
}

bool AES_GCM_CipherContext::IsAvailable()
{
// Libsodium requires AES and CLMUL instructions for AES-GCM, available in
// Intel "Westmere" and up. 90.41% of Steam users have this as of the
// November 2019 survey.
// Libsodium recommends ChaCha20-Poly1305 in software if you've not got AES support
// in hardware.
return crypto_aead_aes256gcm_is_available() == 1;
}

bool AES_GCM_EncryptContext::Encrypt(
const void *pPlaintextData, size_t cbPlaintextData,
const void *pIV,
Expand Down Expand Up @@ -133,6 +144,107 @@ bool AES_GCM_DecryptContext::Decrypt(
return nDecryptResult == 0;
}

/// This implementation uses the IETF variant of the ChaCha20-Poly1305 algorithm from libsodium.
/// For more information, please see https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305/ietf_chacha20-poly1305_construction
bool ChaCha20_Poly1305_CipherContext::InitCipher(const void* pKey, size_t cbKey, size_t cbIV, size_t cbTag, bool bEncrypt)
{
if (cbKey != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
{
AssertMsg(false, "ChaCha20-Poly1305-IETF key sizes other than %d are unsupported.", crypto_aead_chacha20poly1305_ietf_KEYBYTES);
return false;
}
if (cbIV != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
{
AssertMsg(false, "Nonce size is unsupported");
return false;
}

Wipe();

if (pKey == nullptr)
{
AssertMsg(false, "Invalid secret key");
return false;
}

m_ctx = sodium_malloc(cbKey);
memcpy(m_ctx, pKey, cbKey);

m_cbIV = cbIV;
m_cbTag = crypto_aead_chacha20poly1305_ietf_ABYTES;
COMPILE_TIME_ASSERT(crypto_aead_chacha20poly1305_ietf_ABYTES == 16);

return true;
}

bool ChaCha20_Poly1305_CipherContext::IsAvailable()
{
return true;
}

bool ChaCha20_Poly1305_EncryptContext::Encrypt(
const void* pPlaintextData, size_t cbPlaintextData,
const void* pIV,
void* pEncryptedDataAndTag, uint32* pcbEncryptedDataAndTag,
const void* pAdditionalAuthenticationData, size_t cbAuthenticationData
)
{

// Make sure caller's buffer is big enough to hold the result.
if (cbPlaintextData + crypto_aead_chacha20poly1305_ietf_ABYTES > *pcbEncryptedDataAndTag)
{
*pcbEncryptedDataAndTag = 0;
return false;
}

unsigned long long cbEncryptedDataAndTag_longlong;
if (crypto_aead_chacha20poly1305_ietf_encrypt(
static_cast<unsigned char*>(pEncryptedDataAndTag), &cbEncryptedDataAndTag_longlong,
static_cast<const unsigned char*>(pPlaintextData), cbPlaintextData,
static_cast<const unsigned char*>(pAdditionalAuthenticationData), cbAuthenticationData,
nullptr,
static_cast<const unsigned char*>(pIV),
static_cast<const unsigned char*>(m_ctx)
) != 0
) {
AssertMsg(false, "crypto_aead_chacha20poly1305_ietf_encrypt failed"); // docs say this "should never happen"
*pcbEncryptedDataAndTag = 0;
return false;
}

*pcbEncryptedDataAndTag = cbEncryptedDataAndTag_longlong;

return true;
}

bool ChaCha20_Poly1305_DecryptContext::Decrypt(
const void* pEncryptedDataAndTag, size_t cbEncryptedDataAndTag,
const void* pIV,
void* pPlaintextData, uint32* pcbPlaintextData,
const void* pAdditionalAuthenticationData, size_t cbAuthenticationData
)
{
// Make sure caller's buffer is big enough to hold the result
if (cbEncryptedDataAndTag > *pcbPlaintextData + crypto_aead_chacha20poly1305_ietf_ABYTES)
{
*pcbPlaintextData = 0;
return false;
}

unsigned long long cbPlaintextData_longlong = 0;
const int nDecryptResult = crypto_aead_chacha20poly1305_ietf_decrypt(
static_cast<unsigned char*>(pPlaintextData), &cbPlaintextData_longlong,
nullptr,
static_cast<const unsigned char*>(pEncryptedDataAndTag), cbEncryptedDataAndTag,
static_cast<const unsigned char*>(pAdditionalAuthenticationData), cbAuthenticationData,
static_cast<const unsigned char*>(pIV), static_cast<const unsigned char*>(m_ctx)
);

*pcbPlaintextData = cbPlaintextData_longlong;

return nDecryptResult == 0;
}

void CCrypto::Init()
{
// sodium_init is safe to call multiple times from multiple threads
Expand Down
Loading