I am developing an application that is based on UDP, and I need to send a stream of packets. As you can imagine, packets can get lost or corrupted. I need to make sure that the content of those packets is encrypted, and I also need to be able to certify that the packet is correct when I receive it.
Finally, since performance is crucial in my application, I would like to keep the encryption overhead as small as possible. Often times, packets are relatively small, so even a few blocks can build up a relatively big overhead.
I don't have a reliable previous experience with cryptography, so I thought it was better not to trust myself on this.
Here is the strategy I have used until now (my application is far from being released, so having to change it more or less completely won't be so much of a problem).
First of all, packets are of fixed size, which is usually multiple of the block size of AES. I have no need whatsoever for padding, since messages aren't variable in length.
I use a shared key (obviously) and two fixed IVs, each endpoint uses one of the two to encrypt its messages. I use AES in CBC mode.
Since obviously using a fixed IV leads to a whole bunch of security flaws, I need to make my first block vary in a uniformly random fashion: that will act as IV for the next blocks.
Therefore, I prepend a block at the beginning of my packet. Its content goes as follows:
- First four bytes: current timestamp in seconds
- Next 12 bytes: zeros
- I compute the sha256 hash of the message (32 bytes)
- I xor the timestamp + zeros block with the first half of the hash
- I xor the result with the second half of the hash
- When I receive the packet on the other endpoint, I decrypt it with the key and the remote iv.
- I compute the sha256 hash of the message (without the first block), xor the first half and the second half.
- I xor the result of the hashing procedure with the first block.
- If I get a timestamp that is no more than a few seconds ago, and 12 bytes of zeros, then the packet is correct.
- If the packet is not correct, I just drop it, exactly as if I didn't receive it. No answer is sent back.
What are the flaws in this procedure?