Skip to content

Commit 05ffc24

Browse files
committed
Add CCrypto::GenerateMD5Digest
We will need this for TURN auth Implementations: - OpenSSL (EVP only, we don't support the old school stuff) - bcrypt - libsodium doesn't support it because it is insecure. Added a reference implementation to crypto_sha1_wpa.cpp out of laziness Added tests
1 parent 467928d commit 05ffc24

4 files changed

Lines changed: 154 additions & 4 deletions

File tree

src/common/crypto.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class AES_GCM_DecryptContext final : public AES_GCM_CipherContext, public ISymme
113113
namespace CCrypto
114114
{
115115
void Init();
116-
116+
117117
// Symmetric encryption and authentication using AES-GCM.
118118
bool SymmetricAuthEncryptWithIV(
119119
const void *pPlaintextData, size_t cbPlaintextData,
@@ -169,9 +169,12 @@ namespace CCrypto
169169

170170
void GenerateSHA256Digest( const void *pData, size_t cbData, SHA256Digest_t *pOutputDigest );
171171

172+
// You know that MD5s aren't really cryptographically secure right - so this is just for basic integrity checking type stuff
173+
void GenerateMD5Digest( const void *pData, size_t cbData, MD5Digest_t *pOutputDigest );
174+
172175
// GenerateHMAC256 is our implementation of HMAC-SHA256. Relatively future-proof. You should probably use this unless you have a very good reason not to.
173176
void GenerateHMAC256( const uint8 *pubData, uint32 cubData, const uint8 *pubKey, uint32 cubKey, SHA256Digest_t *pOutputDigest );
174-
177+
175178
// GenerateHMAC is our implementation of HMAC-SHA1. The current standard, although people are moving to HMAC-SHA256.
176179
void GenerateHMAC( const uint8 *pubData, uint32 cubData, const uint8 *pubKey, uint32 cubKey, SHADigest_t *pOutputDigest );
177180

src/common/crypto_bcrypt.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ static BCRYPT_ALG_HANDLE hAlgRandom = INVALID_HANDLE_VALUE;
2626
static BCRYPT_ALG_HANDLE hAlgSHA256 = INVALID_HANDLE_VALUE;
2727
static BCRYPT_ALG_HANDLE hAlgHMACSHA256 = INVALID_HANDLE_VALUE;
2828
static BCRYPT_ALG_HANDLE hAlgHMACSHA1 = INVALID_HANDLE_VALUE;
29+
static BCRYPT_ALG_HANDLE hAlgMD5 = INVALID_HANDLE_VALUE;
2930
static ULONG cbBufferSHA256 = 0;
3031
static ULONG cbBufferHMACSHA256 = 0;
3132
static ULONG cbBufferHMACSHA1 = 0;
33+
static ULONG cbBufferMD5 = 0;
3234

3335
typedef struct _BCryptContext {
3436
BCRYPT_ALG_HANDLE hAlgAES;
@@ -97,6 +99,16 @@ void CCrypto::Init()
9799
BCryptGetProperty(hAlgHMACSHA1, BCRYPT_OBJECT_LENGTH, (PUCHAR)&cbBufferHMACSHA1, sizeof(cbBufferHMACSHA1), &garbage, 0 );
98100
AssertFatal( cbBufferHMACSHA1 > 0 && cbBufferHMACSHA1 < 16 * 1024 * 1024 );
99101

102+
BCryptOpenAlgorithmProvider(
103+
&hAlgMD5,
104+
BCRYPT_MD5_ALGORITHM,
105+
nullptr,
106+
0
107+
);
108+
AssertFatal( hAlgMD5 != INVALID_HANDLE_VALUE );
109+
BCryptGetProperty(hAlgMD5, BCRYPT_OBJECT_LENGTH, (PUCHAR)&cbBufferMD5, sizeof(cbBufferMD5), &garbage, 0 );
110+
AssertFatal( cbBufferMD5 > 0 && cbBufferMD5 < 16 * 1024 * 1024 );
111+
100112
return true;
101113
}();
102114
}
@@ -299,6 +311,29 @@ void CCrypto::GenerateHMAC256( const uint8 *pubData, uint32 cubData, const uint8
299311
AssertFatal(NT_SUCCESS(status));
300312
}
301313

314+
//-----------------------------------------------------------------------------
315+
// Purpose: Generate an MD5 hash (used only for TURN long-term credential key derivation)
316+
//-----------------------------------------------------------------------------
317+
void CCrypto::GenerateMD5Digest( const void *pData, size_t cbData, MD5Digest_t *pOutputDigest )
318+
{
319+
VPROF_BUDGET( "CCrypto::GenerateMD5Digest", VPROF_BUDGETGROUP_ENCRYPTION );
320+
Assert( pOutputDigest );
321+
322+
CCrypto::Init();
323+
324+
BCRYPT_HASH_HANDLE hHash = INVALID_HANDLE_VALUE;
325+
PUCHAR pbBuffer = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, cbBufferMD5);
326+
AssertFatal( pbBuffer );
327+
NTSTATUS status = BCryptCreateHash(hAlgMD5, &hHash, pbBuffer, cbBufferMD5, NULL, 0, 0);
328+
AssertFatal(NT_SUCCESS(status));
329+
status = BCryptHashData(hHash, (PUCHAR)pData, (ULONG)cbData, 0);
330+
AssertFatal(NT_SUCCESS(status));
331+
status = BCryptFinishHash(hHash, *pOutputDigest, sizeof(MD5Digest_t), 0);
332+
AssertFatal(NT_SUCCESS(status));
333+
BCryptDestroyHash(hHash);
334+
HeapFree(GetProcessHeap(), 0, pbBuffer);
335+
}
336+
302337
//-----------------------------------------------------------------------------
303338
// Purpose: Generate a keyed-hash MAC using SHA1
304339
//-----------------------------------------------------------------------------

src/common/crypto_digest_opensslevp.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ class EVPCTXPointer
2929
~EVPCTXPointer() { CleanupFunc(ctx); }
3030
};
3131

32+
//-----------------------------------------------------------------------------
33+
// Generate an MD5 hash
34+
//-----------------------------------------------------------------------------
35+
void CCrypto::GenerateMD5Digest( const void *pData, size_t cbData, MD5Digest_t *pOutputDigest )
36+
{
37+
VPROF_BUDGET( "CCrypto::GenerateMD5Digest", VPROF_BUDGETGROUP_ENCRYPTION );
38+
Assert( pOutputDigest );
39+
40+
// NOTE: this will crash if OpenSSL is compiled in FIPS mode,. but I think
41+
// we don't really care about that use case.
42+
43+
EVPCTXPointer<EVP_MD_CTX *, EVP_MD_CTX_free> ctx(EVP_MD_CTX_create());
44+
unsigned int digest_len = sizeof(MD5Digest_t);
45+
VerifyFatal(ctx.ctx != NULL);
46+
VerifyFatal(EVP_DigestInit_ex(ctx.ctx, EVP_md5(), NULL) == 1);
47+
VerifyFatal(EVP_DigestUpdate(ctx.ctx, pData, cbData) == 1);
48+
VerifyFatal(EVP_DigestFinal(ctx.ctx, *pOutputDigest, &digest_len) == 1);
49+
}
50+
3251
//-----------------------------------------------------------------------------
3352
// Generate a SHA256 hash
3453
//-----------------------------------------------------------------------------
@@ -83,7 +102,7 @@ void CCrypto::GenerateHMAC( const uint8 *pubData, uint32 cubData, const uint8 *p
83102
Assert( pubKey );
84103
Assert( cubKey > 0 );
85104
Assert( pOutputDigest );
86-
105+
87106
unsigned int needed = (unsigned int)sizeof(SHADigest_t);
88107
HMAC( EVP_sha1(), pubKey, cubKey, pubData, cubData, (unsigned char*)pOutputDigest, &needed );
89108
}

src/common/crypto_sha1_wpa.cpp

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,98 @@ void CCrypto::GenerateHMAC( const uint8 *pubData, uint32 cubData, const uint8 *p
3030
AssertFatal(status == 0);
3131
}
3232

33-
#endif // #ifdef VALVE_CRYPTO_SHA1_WPA
33+
// Standalone MD5 per RFC 1321. What does thi shave to do with SHA1?
34+
// Absolutely nothing, but the places where we need to use the reference
35+
// implementation of SHA1 just so happen to be the same palces we need
36+
// the reference implementation of MD5, so we are shoving this in here.
37+
static void ComputeMD5( const void *pData, size_t cbData, uint8 pOut[16] )
38+
{
39+
// Per-round shift amounts
40+
static const uint8 r[64] = {
41+
7,12,17,22, 7,12,17,22, 7,12,17,22, 7,12,17,22,
42+
5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20,
43+
4,11,16,23, 4,11,16,23, 4,11,16,23, 4,11,16,23,
44+
6,10,15,21, 6,10,15,21, 6,10,15,21, 6,10,15,21,
45+
};
46+
// K[i] = floor( abs(sin(i+1)) * 2^32 )
47+
static const uint32 K[64] = {
48+
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
49+
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
50+
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
51+
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
52+
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
53+
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
54+
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
55+
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
56+
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
57+
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
58+
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
59+
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
60+
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
61+
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
62+
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
63+
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
64+
};
65+
66+
uint32 h0 = 0x67452301, h1 = 0xefcdab89, h2 = 0x98badcfe, h3 = 0x10325476;
67+
68+
// nChunks covers original data + 0x80 pad byte + 8-byte length field, rounded to 64-byte blocks
69+
const uint64 nBits = (uint64)cbData * 8;
70+
const size_t nChunks = ( cbData + 9 + 63 ) / 64;
3471

72+
for ( size_t chunk = 0; chunk < nChunks; ++chunk )
73+
{
74+
uint32 w[16];
75+
for ( int i = 0; i < 16; ++i )
76+
{
77+
uint32 word = 0;
78+
for ( int j = 0; j < 4; ++j )
79+
{
80+
size_t pos = chunk * 64 + i * 4 + j;
81+
uint8 b;
82+
if ( pos < cbData )
83+
b = ((const uint8 *)pData)[pos];
84+
else if ( pos == cbData )
85+
b = 0x80;
86+
else if ( pos >= nChunks * 64 - 8 )
87+
b = (uint8)( nBits >> ( ( pos - ( nChunks * 64 - 8 ) ) * 8 ) );
88+
else
89+
b = 0;
90+
word |= (uint32)b << ( j * 8 );
91+
}
92+
w[i] = word;
93+
}
94+
95+
uint32 a = h0, b = h1, c = h2, d = h3;
96+
for ( int i = 0; i < 64; ++i )
97+
{
98+
uint32 f, g;
99+
if ( i < 16 ) { f = ( b & c ) | ( ~b & d ); g = i; }
100+
else if ( i < 32 ) { f = ( d & b ) | ( ~d & c ); g = ( 5 * i + 1 ) % 16; }
101+
else if ( i < 48 ) { f = b ^ c ^ d; g = ( 3 * i + 5 ) % 16; }
102+
else { f = c ^ ( b | ~d ); g = ( 7 * i ) % 16; }
103+
uint32 temp = d;
104+
d = c;
105+
c = b;
106+
uint32 rot = a + f + K[i] + w[g];
107+
b = b + ( ( rot << r[i] ) | ( rot >> ( 32 - r[i] ) ) );
108+
a = temp;
109+
}
110+
h0 += a; h1 += b; h2 += c; h3 += d;
111+
}
112+
113+
// Little-endian output
114+
for ( int i = 0; i < 4; ++i ) pOut[i] = (h0 >> (i*8)) & 0xff;
115+
for ( int i = 0; i < 4; ++i ) pOut[4+i] = (h1 >> (i*8)) & 0xff;
116+
for ( int i = 0; i < 4; ++i ) pOut[8+i] = (h2 >> (i*8)) & 0xff;
117+
for ( int i = 0; i < 4; ++i ) pOut[12+i] = (h3 >> (i*8)) & 0xff;
118+
}
119+
120+
void CCrypto::GenerateMD5Digest( const void *pData, size_t cbData, MD5Digest_t *pOutputDigest )
121+
{
122+
VPROF_BUDGET( "CCrypto::GenerateMD5Digest", VPROF_BUDGETGROUP_ENCRYPTION );
123+
Assert( pOutputDigest );
124+
ComputeMD5( pData, cbData, *pOutputDigest );
125+
}
126+
127+
#endif // #ifdef VALVE_CRYPTO_SHA1_WPA

0 commit comments

Comments
 (0)