I am implementing a heartbeat protocol so that a server can send notifications to clients. The notifications are not occurring so often, so I don't want to keep an open TCP+TLS session for each client, since the overhead would be too high. Likewise, I don't want to use DTLS heartbeat (see RFC 6520), since I don't need a payload (and the heartbleed attack makes me think that common implementations are not mature enough).

I thought of the following protocol :

  • First, the client contacts the server over TCP+TLS, and they agree upon an ephemeral key $K$ and a sequence number $S$. The server also attributes an ephemeral session id $Id$ to the client (for faster lookup later). The TCP connection is then closed.

  • Second, the client sends heartbeat messages over UDP, containing $Id || E_K(padding || S)$, where $E$ is a block cipher (e.g. AES), $padding$ is enough zeros so that $padding || S$ has the length of a block, and the sequence number $S$ is increasing between two messages (to avoid replay attacks). Likewise, the server sends $E_K(padding||status||S)$, where $status$ is a bit indicating if a notification is available.

  • If no message is received after some timeout (a few minutes), the server removes the heartbeat session. Also, the session is timed-out after 1 day.

The role of the padding is to ensure that the peer knows the key $K$ (i.e. an attacker should not be able to forge ciphertexts with a valid padding). For example, $S$ could be a $32$-bit number and $padding$ would be $128 - 32 - 1 = 95$ bits (from server to client) if $E$ is AES128.

I am aware that an active attacker (or simply network congestion) can prevent packets from arriving, but apart from that is the scheme secure ? In particular :

  • Is it possible for an attacker to forge a packet with the correct padding (by only seeing the heartbeat packets) ?

  • Is it OK to pad with zeros or is does it induce some weakness ? Likewise, can I use deterministic sequence numbers, starting e.g. at $S = 0$ ?

  • Is the $95$-bit padding long enough (given that the session only lasts a day) ? I could use AES256, but the key is longer, which implies more memory per client on server size. Alternatively, I thought of sending $S$ in clear in the message (i.e. $Id || S || E_K(padding || S)$), which should increase the complexity of a forgery.

  • Is there something else I missed ? Is there a means to improve the protocol in terms of performance ?

Note : the $Id$ that I mentioned would contain $E_k(padding || i)$ with $k$ a key only known of the server and $i$ the index of the client in the table of sessions. This is mainly for performance to avoid using a hash table on the client's IP address and UDP port, and to avoid an attacker to forge valid $Id$s (which could be the basis of a DoS attack with useless lookups in the server's memory). Is this $Id$ implementation secure ?

  • $\begingroup$ At first glance I have a good feeling about it (as long as you don't reuse the keys of course. But it is tricky to answer this as missing something is really easy. Note that together with your protocol you may want to specify really well what you do on failure. $\endgroup$ – Maarten Bodewes Mar 13 '16 at 0:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.