Let's say you're talking about HMAC-SHA256, which is about the only reasonable use of HMAC other than HMAC-SHA512 these days. (For BLAKE2 or SHA-3, you don't need HMAC in the first place; there are native keyed versions.) How is HMAC-SHA256 different from AES-GMAC? (AES-GMAC is AES-GCM with an empty plaintext—the use case you're asking about, giving just an authentication tag.)
The crucial difference is that AES-GMAC requires you to specify a unique message number (nonce) every time you use it with the same key, while (say) HMAC-SHA256 does not.
If you use the same key for two messages with a repeated message number, the security of AES-GMAC totally collapses—it is only designed to be a nonce-based message authentication code. In contrast, HMAC-SHA256 is a good pseudorandom function family safe for more messages than you will ever have. A good PRF always makes a good MAC (without needing a nonce), but the converse is not true in general.
Actually HMAC-SHA256 is considerably more than a PRF: it's also collision-resistant, so it works, e.g., for hash commitments.
There's another important difference: AES-GMAC is either slow and full of timing side channels, or extremely slow, in software implementations, whereas HMAC-SHA256 is merely slow but not likely to have timing side channels. This is because AES and GHASH are both designed to be implemented most efficiently in software with secret-dependent table lookups, which are notoriously leaky. There is hardware acceleration for AES and GHASH, but it takes engineering work to ensure that you are reliably using it in your software stack—if it is available at all. On the other hand, if you have hardware AES and carryless multiplication, then AES-GMAC will blow HMAC-SHA256 out of the water.
If this is a constrained device, you might be interested in a single primitive that does everything like Keccak in STROBE or Gimli in libhydrogen. (Do you really want HMAC in particular, or are you just looking for a message authentication code? HMAC is just one way to construct a PRF out of SHA-256.)
Addressing your specific questions:
- If I need to support AEAD sometimes, is it reasonable to just use AEAD all the time and skip HMAC entirely?
If you only need to detect forgery, and you can assign a unique number to each message, yes. In a sequential conversation, the number of messages you have sent so far is always a good choice—it has the side benefit that you can cheaply reject replays this way. But if you're not in a setting where that makes sense, you might be better off with HMAC-SHA256.
- HMAC should be faster, but how much? I’ll need to test this, but suspect the difference might not be huge.
Why do you think HMAC should be faster?
Here are three relevant points:
Collision resistance—as SHA-256 provides—is generally quite expensive, but if you only need to detect forgery, there's no need for collision resistance. On some platforms SHA-512 is faster than SHA-256, but it still has to pay for collision resistance.
AES-GMAC cost will be dominated by computing GHASH, which means evaluating a message-length polynomial over $\operatorname{GF}(2^{128})$. In software implementations (without hardware carryless multiplication):
- Table-based implementations are likely to have timing side channel attacks.
- Constant-time implementations (without side channel attacks) are likely to be quite slow.
With hardware carryless multiplication, GHASH is extremely fast because you can vectorize the evaluation of a polynomial to an arbitrary degree to maximize utilization of your CPU's vector unit. Only other universal hashes like Poly1305 are competitive—SHA-256 doesn't stand a chance. For example, on my laptop, using openssl speed -evp aes-256-gcm sha256
, I get about 3.5 GB/sec for AES-256-GCM (AES-GCM is basically AES-GMAC + AES-CTR), but 450 MB/sec for SHA-256—about 8x difference in favor of AES-GCM.
So AES-GMAC might be a lot faster or a lot slower than HMAC-SHA256 depending on what hardware you run it on.
For a little more consistency across platforms—including more consistent resistance to timing side channels—I would recommend Poly1305 over GHASH, e.g. by using NaCl/libsodium.
- Is there an advantage to HMAC other than speed and being able to debug data that remains in clear text? Only other thing I know is smaller unpadded payload data.
HMAC-SHA256 does not require you to choose a unique number for each message. HMAC works for more applications. HMAC-SHA256 works for more applications than just message authentication codes, like commitments.
- Slightly aside, but does HMAC or AEAD benefit from including their nonces in the hashed / additional data? My replay attack prevention will mostly revolve around server time delivered to device on connection included in each message.
A nonce is not merely a benefit for AES-GMAC but absolutely mandatory, and server time does not inspire confidence that it will be unique.
Using a sequential nonce with HMAC-SHA256 even though it is not necessary for HMAC-SHA256's security contract enables you to reject replayed messages, and raises the difficulty of forgery attempts because they have to keep in sync with your sequence numbers.