diff --git a/src/common/crypto.h b/src/common/crypto.h index c15fe730..bb7d361b 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -37,6 +37,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(); }; class AES_GCM_EncryptContext : public AES_GCM_CipherContext diff --git a/src/common/crypto_bcrypt.cpp b/src/common/crypto_bcrypt.cpp index 4b94430e..d9cba30b 100644 --- a/src/common/crypto_bcrypt.cpp +++ b/src/common/crypto_bcrypt.cpp @@ -33,6 +33,7 @@ typedef struct _BCryptContext { ULONG cbKeyObject; _BCryptContext() { + hAlgAES = INVALID_HANDLE_VALUE; hKey = INVALID_HANDLE_VALUE; pbKeyObject = NULL; cbKeyObject = 0; @@ -121,6 +122,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, diff --git a/src/common/crypto_libsodium.cpp b/src/common/crypto_libsodium.cpp index d78bd089..3c45c7b9 100644 --- a/src/common/crypto_libsodium.cpp +++ b/src/common/crypto_libsodium.cpp @@ -44,6 +44,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, diff --git a/src/common/crypto_openssl.cpp b/src/common/crypto_openssl.cpp index e31e2a95..6f94a910 100644 --- a/src/common/crypto_openssl.cpp +++ b/src/common/crypto_openssl.cpp @@ -149,6 +149,11 @@ bool AES_GCM_CipherContext::InitCipher( const void *pKey, size_t cbKey, size_t c return true; } +bool AES_GCM_CipherContext::IsAvailable() +{ + return true; +} + bool AES_GCM_EncryptContext::Encrypt( const void *pPlaintextData, size_t cbPlaintextData, const void *pIV, diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp index fd823c2f..cc402752 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp @@ -1037,14 +1037,17 @@ void CSteamNetworkConnectionBase::ClearCrypto() void CSteamNetworkConnectionBase::ClearLocalCrypto() { AssertLocksHeldByCurrentThread(); - m_eNegotiatedCipher = k_ESteamNetworkingSocketsCipher_INVALID; - m_cbEncryptionOverhead = k_cbAESGCMTagSize; m_keyExchangePrivateKeyLocal.Wipe(); m_msgCryptLocal.Clear(); m_msgSignedCryptLocal.Clear(); m_bCryptKeysValid = false; - m_cryptContextSend.Wipe(); - m_cryptContextRecv.Wipe(); + if (m_eNegotiatedCipher != k_ESteamNetworkingSocketsCipher_NULL) + { + m_cryptContextSend.Wipe(); + m_cryptContextRecv.Wipe(); + } + m_eNegotiatedCipher = k_ESteamNetworkingSocketsCipher_INVALID; + m_cbEncryptionOverhead = k_cbAESGCMTagSize; m_cryptIVSend.Wipe(); m_cryptIVRecv.Wipe(); } @@ -1219,19 +1222,29 @@ void CSteamNetworkConnectionBase::SetCryptoCipherList() // V case 0: // Not allowed - m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_AES_256_GCM ); + if (AES_GCM_CipherContext::IsAvailable()) + { + m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_AES_256_GCM ); + } + Assert( m_msgCryptLocal.ciphers_size() > 0 ); break; case 1: // Allowed, but prefer encrypted - m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_AES_256_GCM ); + if (AES_GCM_CipherContext::IsAvailable()) + { + m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_AES_256_GCM ); + } m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_NULL ); break; case 2: // Allowed, preferred m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_NULL ); - m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_AES_256_GCM ); + if (AES_GCM_CipherContext::IsAvailable()) + { + m_msgCryptLocal.add_ciphers( k_ESteamNetworkingSocketsCipher_AES_256_GCM ); + } break; case 3: @@ -1693,13 +1706,16 @@ bool CSteamNetworkConnectionBase::BFinishCryptoHandshake( bool bServer ) V_memcpy( pStart, &expandTemp, sizeof(SHA256Digest_t) ); } - // Set encryption keys into the contexts, and set parameters - if ( - !m_cryptContextSend.Init( cryptKeySend.m_buf, cryptKeySend.k_nSize, m_cryptIVSend.k_nSize, k_cbAESGCMTagSize ) - || !m_cryptContextRecv.Init( cryptKeyRecv.m_buf, cryptKeyRecv.k_nSize, m_cryptIVRecv.k_nSize, k_cbAESGCMTagSize ) ) + if (m_eNegotiatedCipher != k_ESteamNetworkingSocketsCipher_NULL) { - ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Remote_BadCrypt, "Error initializing crypto" ); - return false; + // Set encryption keys into the contexts, and set parameters + if ( + !m_cryptContextSend.Init( cryptKeySend.m_buf, cryptKeySend.k_nSize, m_cryptIVSend.k_nSize, k_cbAESGCMTagSize ) + || !m_cryptContextRecv.Init( cryptKeyRecv.m_buf, cryptKeyRecv.k_nSize, m_cryptIVRecv.k_nSize, k_cbAESGCMTagSize ) ) + { + ConnectionState_ProblemDetectedLocally( k_ESteamNetConnectionEnd_Remote_BadCrypt, "Error initializing crypto" ); + return false; + } } //