File chunk HMACs: Are they verified in ordinary operation?

I was wondering if, in ordinary operation, the 32k file chunks’ HMACs are verified during decryption

Using the excellent architecture document, I’ve written a C# program to decrypt a Cryptomator file. Though the decryption works fine, I can’t get the chunk HMACs to match the expected values. The code looks like this:

                bchMac.Init(new KeyParameter(hmackey));
                var bcHash = new byte[bchMac.GetMacSize()];
                bchMac.BlockUpdate(headerNonce, 0, headerNonce.Length); // 16 bytes
                bchMac.BlockUpdate(beBlockNum, 0, beBlockNum.Length); // 8 big-endian
                bchMac.BlockUpdate(chunkNonce, 0, chunkNonce.Length); // 32 bytes
                bchMac.BlockUpdate(chunkpayload, 0, chunkpayload.Length); // 32768
                bchMac.DoFinal(bcHash, 0);

Resulting hash does not does not match the last 32 bytes of the file content chunk where the expected mac value is supposed to be. But again, the decrypted file is perfect.

Thanks for any insight.

Yes, the MAC must be verified before decryption to avoid CCAs. We have written several unit tests to make sure that decryption fails, if any part of the chunk is modified.

For a reference, here is the Java code that is responsible for chunk mac verification.

The order of headerNonce, blockNum, chunkNonce and chunkpayload looks correct. What does the second parameter of BlockUpdate do?

I assume bchMac is a HMAC-SHA256? Does it produce the expected results for the HMAC-SHA-256 test vectors?

Solved. This was a weird one.

What was happening is that the file header nonce was being corrupted. The AesCtr implementation I was using was updating the IV value (the header nonce) when I decrypted the file header, causing the MAC validation for the chunks to fail. Using a cloned copy of the nonce for decryption solved the problem.

Thanks for the response!

In answer to your questions, bchMac is Bouncy Castle HMACSHA256 implementation. I was originally using built-in .Net but moved to Bouncy to see if it would solve my problem. The 0 parameter is the index into the source byte array. I’ve since moved back to straight .Net.

1 Like