UUIDs aren’t big when stored properly (ie not as strings); I just got over it. Am I missing something?
This "SIV" mode is silly and breaks down completely when encrypting more than 2^32 IDs.
Your proposal is not only faster, but also safer. AES is a strong pseudorandom permutation, the 00000000 padding is a perfectly fine integrity check.
You're right (if you add a constant-time check upon decryption that the bits are zero).
I suggested as much here yesterday, and may revise the scheme to do so:
https://www.reddit.com/r/crypto/comments/fyn8cs/aesbased_syn...
The usual use case for a constant time comparison is to avoid allowing an attacker to guess a value one byte at a time. For example, a 16 byte MAC value on a packet could be brute forced byte-by-byte if the system reveals to the attacker which byte the MAC check failed on.
But in the zero padded AES case, the check is being done after an AES decryption operation, so the attacker can't attempt to generate each zero value a byte at a time. After 256 guesses, an attacker able to see timing information could figure out when their 128 bit input guess generated a decrypted value with a valid zero in the first padding byte. But that does them no good trying to generate a zero in the next padding byte because any change they make to their input guess changes all the decrypted output bytes, including that first zero. Each successive zero byte means they have to start over, so generating 8 zero bytes requires 2^64 guesses as far as I can tell.
pk = decrypt(params.id)
if pk[0:8] != EIGHT_ZEROS:
return Http404
id = int(pk[8:16])
object = db.query(id)
Also stuff like this isn't really specific to using this particular construction. Even if systems are designed to return "does not exist" instead of "forbidden", it's hard to make authorization checks constant time and I've never seen code to even try that.EDIT: actually I don't believe that my above scheme is any better than your original as long as you verify the zero padding is correct.
Because SID encrypts the 64-bit ID using counter mode, not validating the SIV value allows an attacker to make specific changes to the decrypted ID (e.g. flipping individual bits) or inserting chosen ID values if the attacker ever learns the mapping between an 128-bit encrypted ID and the decrypted 64-bit value.
Even if you don't validate the zero padding in the other approach, an attacker is only able to get the system to accept random 64-bit ID values as far as I can tell. Still not great, but less catastrophic and it's not a malleability problem as I understand the term because the attacker is unable to make specific changes in the decrypted output.
newid = AES-EncryptBlock(0||id)
0||id = AES-DecryptBlock(newid)
Check for 8 zero bytes for authentication. Bonus: can use a constant instead of zeros for domain separation (eg different DB columns or tables).