From ccf0a2ed4da6701f376666620ea702dcc977d1bf Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Mon, 5 May 2014 10:35:31 -0700 Subject: [PATCH] More secure comparison of HMAC strings To avoid timing attacks. The implementation of SecureCompareStrings is pulled directly from python 3.4 sources. --- python/ycm/utils.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/python/ycm/utils.py b/python/ycm/utils.py index 095337c9..082e64a8 100644 --- a/python/ycm/utils.py +++ b/python/ycm/utils.py @@ -215,10 +215,33 @@ def SafePopen( *args, **kwargs ): def ContentHexHmacValid( content, hmac, hmac_secret ): - return hmac == CreateHexHmac( content, hmac_secret ) + return SecureCompareStrings( CreateHexHmac( content, hmac_secret ), hmac ) def CreateHexHmac( content, hmac_secret ): return hmac.new( hmac_secret, msg = content, digestmod = hashlib.sha256 ).hexdigest() + + +# This is the compare_digest function from python 3.4, adapted for 2.7: +# http://hg.python.org/cpython/file/460407f35aa9/Lib/hmac.py#l16 +def SecureCompareStrings( a, b ): + """Returns the equivalent of 'a == b', but avoids content based short + circuiting to reduce the vulnerability to timing attacks.""" + # Consistent timing matters more here than data type flexibility + if not ( isinstance( a, str ) and isinstance( b, str ) ): + raise TypeError( "inputs must be str instances" ) + + # We assume the length of the expected digest is public knowledge, + # thus this early return isn't leaking anything an attacker wouldn't + # already know + if len( a ) != len( b ): + return False + + # We assume that integers in the bytes range are all cached, + # thus timing shouldn't vary much due to integer object creation + result = 0 + for x, y in zip( a, b ): + result |= ord( x ) ^ ord( y ) + return result == 0