Thinktecture.IdentityModel: Comparing Strings without leaking Timinig Information

Posted by Your DisplayName here! on Least Privilege See other posts from Least Privilege or by Your DisplayName here!
Published on Sat, 08 May 2010 19:51:07 GMT Indexed on 2010/12/06 17:00 UTC
Read the original article Hit count: 474

Filed under:

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;
}

© Least Privilege or respective owner

Related posts about IdentityModel