Publicly Verifiable Authenticator I am working on a Block Storage system, where I have the need to encrypt and authenticate blocks by a client, but also want the server to be able to detect bit corruption while minimizing the work done on the data. The storage blocks are roughly 1MB segments.

One option is to let the client use a AE(AD) cipher (like AES/GCM) and afterwards add a strong checksum. Since AES/GCM is not yet the most optimized cipher (in my Java world), an alternative would be to use a more traditional encrypt-then-HMAC. However, with a normal HMAC, this could not serve as a publicly verifiable checksum. And calculating both, a HMAC and a checksum would double the amount of work which needs to be done (over the message). What about only calculating the checksum over the message, and using a HMAC on this reduced data set?

encrypted = AESCBC(IV, plaintext-1MB)
checksum = SHA224(ID | IV | len | encrypted)
sig = HMAC(ID | len | checksum)
message = ID | IV | len | encrypted | checksum | sig


In this case I calculate a publicly verifiable checksum over the data (so I can detect corruption on the server) and only the client could check in addition to that the HMAC authenticator added. The HMAC does not need to run over all the encrypted data, but uses the checksum as a shortcut. The ID is unique for each segment.

In my case the protocol can't be used as an anonymous oracle and the plaintext length can not be extended boundlessly, however a hash which is more resistant against extension attacks is intentionally used.

I haven't seen much analysis on using ordinary hashes for content authentication. Can anyone can give me a pointer on whether this is safe? (I haven't yet done performance analysis yet; I might simply go with AES/GCM+SHA if that is fast enough). Or is there a more standard construction for generating a publicly verifiable and authenticating tag?
I haven't seen much analysis on using ordinary hashes for content authentication. Can anyone can give me a pointer on whether this is safe?


With good choice of primitives it is. Public key signatures use a hash function in a similar way to identify the message signed.

However, where a MAC only needs what amounts to second preimage resistance, a hash function also typically needs collision resistance, since an attacker can calculate an arbitrary amount on their own. (This is why SHA-1 is being deprecated in signatures even though there's no indication it isn't preimage resistant.)

HMAC is also stronger than the hash function it uses. So while SHA-224 is strong, you will have lower security with it than with HMAC-SHA-2. Technically, it may mean you don't even have 128-bit security – "only" 112-bit.

You don't actually need to use the checksum itself in the HMAC. You can instead use a full SHA-256 (or SHA-512, which may be faster). If you want to truncate – length extension attacks aren't an issue because of the HMAC – only truncate it when writing the checksum, but not when calculating the HMAC. Assuming you don't need as much confidence when detecting corruption, you could even truncate the checksum to only a few bytes and save some space.

I.e.

hash = SHA-NNN(ID | IV | len | encrypted)
checksum = truncate(hash)
sig = HMAC(ID | len | hash)
message = ID | IV | len | encrypted | checksum | sig


(Having ID and len in the HMAC seems unnecessary, but doesn't hurt.)