3
$\begingroup$

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?

$\endgroup$
4
  • $\begingroup$ 1. Don't roll your own crypto. What's wrong with TLS? 2. Encrypt-then-MAC (and please use a proper MAC) 3. Use AES-GCM 4. Keep it simple. $\endgroup$
    – SEJPM
    Jul 7, 2015 at 17:03
  • $\begingroup$ 5. What would stop an adversary from manipulating clocks? $\endgroup$
    – SEJPM
    Jul 7, 2015 at 17:05
  • 1
    $\begingroup$ packets are "usually multiple of the block size of AES"? You can't say usually then say that you "have no need whatsoever for padding". $\endgroup$
    – mikeazo
    Jul 7, 2015 at 17:08
  • $\begingroup$ Why not just use TCP instead and then just put TLS on top of it? You would literally even be better off rolling your own reliability on top of UDP than rolling your own crypto scheme. If TCP or other reliable transport really isn't an option, try DTLS, as mentioned in one of the answers. $\endgroup$
    – reirab
    Jul 7, 2015 at 21:52

2 Answers 2

4
$\begingroup$

The "interesting" part of your encryption is here:

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

Since XOR is commutative and associative, order does not matter and you are XORing the zero-padded timestamp with a hash from a function defined as the XOR of the two halves of SHA-256.

This should ring some warning bells. There is a reason SHA-256 is 256 bits long, and that's because collision resistance of a hash is only $n/2$ bits. By halving the length of the hash (which you could equally strongly do by truncation) you reduce its collision resistance to 64 bits, which is not considered strong enough.

Now, if you only relied on the preimage or second preimage resistance of the hash function, you would be OK. However, the security of encrypted hash as a MAC relies on its collision resistance.

Instead, you could just use a normal MAC (like HMAC), preferably in encrypt-then-MAC. Or better yet, an authenticated encryption mode (like GCM).

That does not deal with the lack of IV, however. If you continue using CBC, you can just generate a random number for that purpose, if you must, no need to get fancy. CTR-based modes, like GCM, require a unique nonce and cannot recover this way from a duplicate.


Your options, from most to least recommendable are:

  1. Don't roll your own protocol, but use (D)TLS.
  2. If you must roll your own protocol, use an authenticated encryption mode.
  3. If you can't, at least use standard encryption and MAC.
$\endgroup$
10
  • $\begingroup$ Thank you very much! Could you point me to something more specific? What strategy would you use? And how many bytes of crypto header would that mean? $\endgroup$ Jul 7, 2015 at 17:29
  • $\begingroup$ Does hmac keep track of the timestamp as well? Are there other strategies to certify that the message has just been sent rather than sent, intercepted, kept and sent after a while? $\endgroup$ Jul 7, 2015 at 17:31
  • $\begingroup$ (I didn't want to generate a new IV every time because I would need to send it along with the message... right? That would mean say 16 bytes for the IV, 16 bytes for the MAC, and possibly more...?) $\endgroup$ Jul 7, 2015 at 17:33
  • $\begingroup$ @MatteoMonti, simplest would be to just use AES in an authenticated-encryption mode, like GCM. You can influence the amount of data expansion with your choice of counter and tag lengths, but less than 256 bits. You would need to include the timestamp, if you want one, inside the encrypted data. $\endgroup$
    – otus
    Jul 7, 2015 at 17:33
  • $\begingroup$ GCM. Ok. OpenSSL has a library for that, right? Does that produce both the encrypted message and MAC? Do I also need to randomize a new IV every time and send it along with the message? $\endgroup$ Jul 7, 2015 at 17:35
6
$\begingroup$

At a high level, the major flaw is that you are rolling your own crypto protocol. You should strongly consider using a standardized protocol like DTLS.

Some specific problems:

  1. Symmetric key distribution is left unspecified. Keys must be changed occasionally to thwart distinguishers.
  2. No way to recover from symmetric key compromise.
  3. Your message authentication code follows no existing standards and therefore is likely not secure.
  4. If the attacker can get the IV, which is fixed, so likely not too hard, and can influence the first block of plaintext (which wouldn't be too hard since you are using timestamps), they can do some nasty things.
  5. The fixed IVs add complexity that is unnecessary. For example, you have to trust the endpoints to not get the IVs mixed up.

There are probably a lot more issues that I haven't thought of in the short time I've been thinking about it. To solve this, use something standardized.

$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.