Thinktecture.IdentityModel: Comparing Strings without leaking Timinig Information
- by Your DisplayName here!
Paul Hill commented on a recent post where
I was comparing HMACSHA256 signatures. In a nutshell his complaint was that I am leaking
timing information while doing so – or in other words, my code returned faster with
wrong (or partially wrong) signatures than with the correct signature. This can be
potentially used for timing attacks like this one.
I think he got a point here, especially in the era of cloud computing where you can
potentially run attack code on the same physical machine as your target to do high
resolution timing analysis (see here for
an example).
It turns out that it is not that easy to write a time-constant string comparer due
to all sort of (unexpected) clever optimization mechanisms in the CLR. With the help
and feedback of Paul and Shawn I came up with this:
Structure the code in a way that the CLR will not try to optimize it
In addition turn off optimization (just in case a future version will come up with
new optimization methods)
Add a random sleep when the comparison fails (using Shawn’s and Stephen’s nice Random wrapper
for RNGCryptoServiceProvider).
You can find the full code in the Thinktecture.IdentityModel download.
[MethodImpl(MethodImplOptions.NoOptimization)]
public static bool IsEqual(string s1, string s2)
{
if (s1
== null && s2 == null)
{
return true;
}
if (s1
== null || s2 == null)
{
return false;
}
if (s1.Length
!= s2.Length)
{
return false;
}
var s1chars
= s1.ToCharArray();
var s2chars
= s2.ToCharArray();
int hits
= 0;
for (int i
= 0; i < s1.Length; i++)
{
if (s1chars[i].Equals(s2chars[i]))
{
hits
+= 2;
}
else
{
hits
+= 1;
}
}
bool same
= (hits == s1.Length * 2);
if (!same)
{
var rnd
= new CryptoRandom();
Thread.Sleep(rnd.Next(0,
10));
}
return same;
}