8

i am asking myself if it would be dangerous on a asp.net page´s codebehind to use an static (Shared) variable which holds an HMACSHA1-instance. the problem is that the same HMACSHA1-instance would be used by all the asp.net worker-process´ threads when processing multiple simultaneous requests on the same asp.net-page. all (HMACSHA1)instance- and ComputeHash()-method variables which are used/modified by ComputeHash() would be shared (= could be modified) by all the threads?! is that assumption correct? as a result the return value of ComputeHash would not be guaranteed to be correct?!?! thus i am not allowed to use an static/Shared HMACSHA1-instance over all the asp.net-threads..

i am just wondering what you think about this problem.

the only solution to this would be sth like an critical path etc in the ComputeHash()-method. but that is "out of our reach"..

regards, kris

Hank Gay
  • 70,339
  • 36
  • 160
  • 222

4 Answers4

10

I just got an unknown cryptographic exception form the SHA256Cng.ComputeHash, when running 8 or 16 parallel tasks that amongst other things performed the hash computation, on a quad-core HT cpu.

Adding locking semantics around ComputeHash solved the issue - so it seems that at least the SHA256Cng version is not thread safe.

Molokai
  • 101
  • 1
  • 2
6

It worth to know that KeyedHashAlgorithm.ComputeHash() is not thread safe because it give non-deterministic result for the same KeyedHashAlgorithm.Key.

In my case, I want to cache KeyedHashAlgorithm since my KeyedHashAlgorithm.Key is always the same to verify the authenticity from client side. I realize that ComputeHash() is not consistent, probably it cache internal variable into the KeyedHashAlgorithm instance. I should cache the instance per thread ThreadStatic or ThreadLocal. This is the test:

Static KeyedHashAlgorithm gives inconsistent result:

var kha = KeyedHashAlgorithm.Create("HMACSHA256");
kha.Key = Encoding.UTF8.GetBytes("key");
Action comp = () =>
{
    var computed = kha.ComputeHash(Encoding.UTF8.GetBytes("message"));
    Console.WriteLine(Convert.ToBase64String(computed));
};
Parallel.Invoke(comp, comp, comp, comp, comp, comp, comp, comp);

Compared to KeyedHashAlgorithm per thread:

ThreadLocal<KeyedHashAlgorithm> tl= new ThreadLocal<KeyedHashAlgorithm>(() =>
{
    var kha = KeyedHashAlgorithm.Create("HMACSHA256");
    kha.Key = Encoding.UTF8.GetBytes("key");
    return kha;
});
Action comp = () =>
{
    var computed = tl.Value.ComputeHash(Encoding.UTF8.GetBytes("message"));
    Console.WriteLine(Convert.ToBase64String(computed));
};
Parallel.Invoke(comp, comp, comp, comp, comp, comp, comp, comp);

This code can be use to test other functions for 'thread safety' result. Hope this will help others.

CallMeLaNN
  • 8,328
  • 7
  • 59
  • 74
  • Hey I was trying out the inconsistent result function. If you new up the HashAlgorithm inside the action 'comp' instead of outside then it is consistent again. Do you know why this is? – CRice Apr 10 '17 at 02:07
  • It's been a while. I didn't remember much. You are experimenting the code. Try to understand the Parallel.Invoke. If you initiate new HashAlgorithm inside the comp, it is the same like you create it every time you want to use it. Defeat the purpose of OP to cache the instance. Based on my code above, I can cache one instance per thread to get consistent result. – CallMeLaNN Apr 10 '17 at 16:49
6

Hashing algorithms are deterministic, they must return the same hash for a given input every time.

As long as you use the same key each time then there's no need to make them static. However if you're constructing the HMACSHA1 instance with the parameterless constructor then it generates a random key. You should be taking the random value from the KeyValue property and storing it with the hash.

It is definitely dangerous to use a static instance. If Thread1 sets the value to be calculated, and then Thread2 sets the value before Thread1 calls ComputerHash() then Thread1 is going to get the hash of Thread2's value. The same will happen if either thread is setting the key.

blowdart
  • 55,577
  • 12
  • 114
  • 149
  • 1
    Hashing algorithms indeed have to be deterministic.. but problem is multithreading :) How do you mean: "..no need to make them static"? The key is remaining the same all the thime and will be periodically (monthly) changed. I am creating the instance like this: HMAC hmac = HMAC.Create("HMACSHA1"); –  Feb 20 '09 at 14:42
  • The reason why i would make the HMAC-variable static to the asp.net-codebehind-class is performance.. but, otherwise, ComputeHash() should have nearly about 100% of it´s content in between an critical section to guarantee thread-safety.. –  Feb 20 '09 at 14:44
  • ..so my idea to hold the hmac instance as static would be good to save memory maybe, but each page-request would take longer than with non-static hmac-Instances, because of the locking-mechanism. –  Feb 20 '09 at 14:47
  • Well, benchmark it, but my guess is that in a typical ASP.NET page request, the overhead of one more CSP operation might not be too important. – MichaelGG Feb 20 '09 at 15:18
  • 1
    It sounds like premature optimisation here to me, and the lock issues would mean if your machine is heavily hit every page requesting an HMAC is going to be waiting. Horrible! – blowdart Feb 20 '09 at 15:49
  • Thank you guys for the conversation on this.. i think, it is worth every consideration when it´s about optimisation and it´s also good to understand .NET a little better :) Locking would not be good.. thats right. Like MichaelGG said, only benchmarking would let us know more. Keep your good work! –  Feb 20 '09 at 16:57
2

If you want thread safety without locking, you can use the ThreadStatic attribute to create a unique instance on each thread like this:

[ThreadStatic]
private static HMACSHA1 _hmacSha1;

public static HMACSHA1 HmacSha1
{
    get 
    {
        if (_hmacSha1 == null)
        {
            // this will happen once on each thread
            _hmacSha1 = new HMACSHA1(GetKeyBytes());
        }               

        return _hmacSha1;
    }
}

Now, two side notes:

  1. Accessing a thread-static field takes significantly longer than accessing a normal static field. So the thread-static version may or may not be better for you.

  2. If you're doing this once per page request, then the difference will be so minuscule that it won't matter which approach you choose. If you were doing this in a very tight loop, or the code in your lock section took a very long time, then the choice could be important.

Neil
  • 7,227
  • 5
  • 42
  • 43