If the receiver can wait for all the packets before decrypting:
This case is simple, since your final goal is to ensure that the plaintext you decrypt was the exact same plaintext you encrypted. (Trivially, this includes rejecting re-ordered plaintext.) Use an Authenticated Encryption (AE) scheme (eg, CCM, GCM, etc) across all the packets, treating the packet contents as concatenated parts of the same message. The receiver waits for all packets to arrive then authenticates and decrypts them. If the authentication fails, the message is different in some way, possibly because the contents are re-ordered, possibly something else.
If the receiver must decrypt packets as they arrive:
Encrypt each packet individually. Use an AE scheme that supports additional authenticated data, such as CCM or GCM. Such schemes are designed to encrypt a secret payload and authenticate the same secret payload alongside plaintext data, which is often times metadata relevant to the ciphertext. If the authentication passes, the receiver can be sure that the secret payload corresponds with the plaintext metadata and that both are unaltered.
This allows you to encrypt your plaintext $M$ as $C=E(M)$, authenticate the ciphertext $C$ along with a plaintext packet number/counter $ctr$, then send $(C,ctr)$ to the receiver. The receiver verifies that $(C,ctr)$ properly authenticates, meaning that $ctr$ belongs to that package.
Presumably the packet number isn't a secret since an attacker can probably watch the wire and count packets anyway. Nonetheless, the packet number $ctr$ can be omitted from the data the sender transmits and the receiver can just substitute the $ctr$ value that they are expecting when they receive the packet and perform the authenticate.
The Proposed Schemes
This assumes you're using CBC for encryption and HMAC for authentication.
This is a good option. In concept, you've created a very simple self-contained protocol for your data that encapsulates ordering of the packets. Then the packets are simply encrypted and authenticated. It's the simplest scheme, and the least likely to be screwed up.
This is essentially building an AE scheme for ciphertext and additional associated data (message-number
is the associated data). Likely encrypted-message
is variable length, but message-number
is presumably fixed length. If it is fixed length this should work, if it's variable length this would require modification.
This is potentially risky. The IV for a CBC-encrypted message should be unpredictable and you're working in discrete packets. If the attacker can control what data is in a packet and has seen the previous packet, they will know the IV for the packet they are about to generate and can use that to launch attacks. There was a popular TLS/SSL attack that exploited this type of CBC IV chaining.
This option is actually kind of pointless. It requires that the original random IV be sent along with the derived (aka, "actual") IV, both MAC'd alongside the ciphertext. Verifying message-number
would consist of re-deriving the real IV using the random IV and the expected message-number
. It's essentially option (2): they both have an IV, message-number
-based value, and the ciphertext.
(To see why both IVs must be sent: If just the original random IV is sent, we can't validate that the derived IV is correct. The HMAC covers only the ciphertext and random IV, so after that the HMAC does the check the packet receiver will derive the real IV and have nothing to check it against. If message-number
is wrong then the derived IV is wrong, and the first block of plaintext is trashed, but a trashed first block of plaintext is not always detectable.
If just the derived IV is sent, the recipient can't validate what message-number
the derived IV is for. The HMAC will validate the derived IV and the ciphertext, but the receiver will just decrypt using the IV, knowing nothing about what message-number
should have been used to generate it.)
This would work, but it is pretty much the same as option (1) if you send the "actual" IV, since the receiver needs to check the message-number
by decrypting the IV (option (1) would require decrypting the plaintext).
Of those listed, option (1) is probably the best. It isn't the cryptography itself enforcing the message ordering, rather the "protocol" encapsulated within the packets. Option (2) seems perfectly reasonable as well.