I'm designing a protocol for use into a VPN software.
The VPN frames are encapsulated into AES-256 CBC encrypted frames. I understand that IVs must be uniquely used for each message encrypted with the same key. I believe that usually protocols generate those randomly and transmit them (in cleartext, since those are no supposed to be secret) with the ciphertext.
In my case, I wanted to avoid that because transmitting 16 bytes of IV for each VPN frame has a huge cost in bandwidth (in some cases, the size of the headers would exceed the size of the encapsulated frames) so I decided to compute those IV instead, using the following mechanism:
4.4.1. DATA messages initialization vectors
Each DATA message relies on the session keys and on an initialization
vector. This initialization vector MUST be unpredictable.
That is, it is computed for each DATA message using a concatenation
of the current session and sequence numbers. The concatenation MUST
have the following layout (numbers are in network byte order):
0 7 8 15 16 23 24 31
+-----------------------------------+
| session_number |
+-----------------------------------+
| sequence_number |
+-----------------------------------+
| must_be_zero |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
The must_be_zero field is a sequence of 0x00 bytes so that the whole
frame is the size of a block for the selected cipher algorithm.
The concatenation buffer, which is globally unique, is then ciphered
using the target host encryption key (KE) without padding and using a
null initialization vector.
The resulting buffer is the initialization vector to use to (de)cipher
the DATA message.
The complete protocol specification is available here if anyone wants to read it.
The session_number is constant for a given session (it has the same lifetime than the AES keys of the session) and the sequence number is a zero-initialized counter incremented on each send.
Doing so, I believe I have an unpredictable IV (because the attacker would have to know KE to predict the IV) and that this IV is unique (because it depends on the sequence_number which changes for each message).
I realized lately that this method was expensive in CPU time and was the cause of performance issues (because of the hash computation that seems to be the bottleneck).
So I do have two questions here:
- Is my method correct (read "secure") ?
- How could I do differently to avoid computing a hash for each IV while keeping unpredictability and uniqueness ?