Per Erik Gransøe
Per Erik Gransøe

Reputation: 489

BouncyCastle HMAC SHA1

I have the following code using BouncyCastle (dotnet version) to get a HMAC-SHA1 from a message.

I have this small library class:

public class HashingTools
{
    static string hmacKey = "81310910a060c5705c1d3cedf370bcf9";
    public static int HashSizeInBytes = 20;
    static KeyParameter keyParameter = null;
    private static HMac hmacInstance;

    static HashingTools()
    {
        hmacInstance = new HMac(new Sha1Digest());
        hmacInstance.Init(newKeyParameter(Encoding.UTF8.GetBytes(hmacKey)));
    }

    public static byte[] HashSha1(byte[] message)
    {
        byte[] result = new byte[hmacInstance.GetMacSize()];

        hmacInstance.BlockUpdate(message, 0, message.Length);
        hmacInstance.DoFinal(result, 0);

        return result;
    }
}

And I have a lot of messages going through this method, all using the same key: hmacKey, and I would like to speed it up as much as I can, and reuse as much as I can, still with respect to security parameters (randomness, freshness...).

If I try to reuse or parallellize the hmac instance, I get an "array out of bounds" exception inside Org.BouncyCastle.Crypto.Macs.Hmac.BlockUpdate.

I've created this unittest for reproduction (1 or 2 parallel hash functions goes fine, 100 goes wrong):

[Test]
public void TestBulkHashing()
{
    var messages = new List<byte[]>();

    foreach (var index in Enumerable.Range(0, 100))
    {
        var buffer = new byte[4096];
        Random r = new Random();
        r.NextBytes(buffer);

        messages.Add(buffer);
    }

    Parallel.ForEach(messages, m =>
    {
        HashingTools.HashSha1(m);
    });
}

Upvotes: 3

Views: 1144

Answers (1)

President James K. Polk
President James K. Polk

Reputation: 42009

As correctly surmised by @dlatikay, this is a synchronization mistake. Bouncycastle's classes are not thread-safe unless they explicitly say it is.

If you modify your HashSha1 method to explicitly synchronize threads you will not get the exception:

public static byte[] HashSha1(byte[] message) {
    byte[] result = new byte[hmacInstance.GetMacSize()];
    lock(hmacInstance) {
        hmacInstance.BlockUpdate(message, 0, message.Length);
        hmacInstance.DoFinal(result, 0);
    }

    return result;
}

As to your question about optimization, Bouncycastle already pre-computes the part of the computation that involves the key. When you call DoFinal(...) the internal state is reset to this pre-computed value so you do not need to call Init() again for the next HMac if you use the same key. Your code already takes advantage of this optimization so I don't think there is more you can do unless you want to write your own hashing code.

Upvotes: 1

Related Questions