I understand that the main motivation for HMAC is extension attacks. If data always has content-length either implicit in the app or even explicitly put into the hash, I get the impression that HMAC is not necessary. The motivation is to allow password based encryption of keys, using widely available primitives, and periodic rotating of master-password without re-encrypting data (but re-encrypting keys):
file.name = "foo.txt"
file.iv = freshRandom256Bits()
filekey = freshRandom256Bits() #only master learns this
#encrypted key
file.encryptedKey = sha256(masterpwd+":"+file.iv) ^ filekey
#make it hard to forge data
file.auth = sha256(masterpwd+":"+file.owner+":"+file.encryptedKey)
#write data stream and file meta, then filekey goes out of scope
writeEncryptedIO(file, filekey)
In this case, the encryptedKey
has a known length. In that case, HMAC is not strictly necessary, no? If I did use an HMAC variant like:
$$h(k || h(k || data))$$
Does it really buy me anything? Is it OK to omit the XOR with constants that are prescribed? I'm under the impression that the whole reason HMAC is good is that it solves the extension problem by making the thing appended to k
be of a known length.