Next: , Previous: , Up: Setting up the transport layer   [Contents][Index]


6.5.4 Anti-replay protection

When 0-RTT mode is used, the server must protect itself from replay attacks, where adversary client reuses duplicate session ticket to send early data, before the server authenticates the client.

GnuTLS provides a simple mechanism against replay attacks, following the method called ClientHello recording. When a session ticket is accepted, the server checks if the ClientHello message has been already seen. If there is a duplicate, the server rejects early data.

The problem of this approach is that the number of recorded messages grows indefinitely. To prevent that, the server can limit the recording to a certain time window, which can be configured with gnutls_anti_replay_set_window.

The anti-replay mechanism shall be globally initialized with gnutls_anti_replay_init, and then attached to a session using gnutls_anti_replay_enable. It can be deinitialized with gnutls_anti_replay_deinit.

The server must also set up a database back-end to store ClientHello messages. That can be achieved using gnutls_anti_replay_set_add_function and gnutls_anti_replay_set_ptr.

Note that, if the back-end stores arbitrary number of ClientHello, it needs to periodically clean up the stored entries based on the time window set with gnutls_anti_replay_set_window. The cleanup can be implemented by iterating through the database entries and calling gnutls_db_check_entry_expire_time. This is similar to session database cleanup used by TLS1.2 sessions.

The full set up of the server using early data would be like the following example:

#define MAX_EARLY_DATA_SIZE 16384

static int
db_add_func(void *dbf, gnutls_datum_t key, gnutls_datum_t data)
{
    /* Return GNUTLS_E_DB_ENTRY_EXISTS, if KEY is found in the database.
     * Otherwise, store it and return 0.
     */
}

static int
handshake_hook_func(gnutls_session_t session, unsigned int htype,
                    unsigned when, unsigned int incoming, const gnutls_datum_t *msg)
{
    int ret;
    char buf[MAX_EARLY_DATA_SIZE];

    assert(htype == GNUTLS_HANDSHAKE_END_OF_EARLY_DATA);
    assert(when == GNUTLS_HOOK_POST);

    if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA) {
        ret = gnutls_record_recv_early_data(session, buf, sizeof(buf));
        assert(ret >= 0);
    }

    return ret;
}

int main(void)
{
  ...
  /* Initialize anti-replay measure, which can be shared
   * among multiple sessions.
   */
  gnutls_anti_replay_init(&anti_replay);

  /* Set the database back-end function for the anti-replay data. */
  gnutls_anti_replay_set_add_function(anti_replay, db_add_func);
  gnutls_anti_replay_set_ptr(anti_replay, NULL);

  ...

  gnutls_init(&server, GNUTLS_SERVER | GNUTLS_ENABLE_EARLY_DATA);
  gnutls_record_set_max_early_data_size(server, MAX_EARLY_DATA_SIZE);

  ...

  /* Set the anti-replay measure to the session.
   */
  gnutls_anti_replay_enable(server, anti_replay);
  ...

  /* Retrieve early data in a handshake hook;
   * you can also do that after handshake.
   */
  gnutls_handshake_set_hook_function(server, GNUTLS_HANDSHAKE_END_OF_EARLY_DATA,
                                     GNUTLS_HOOK_POST, handshake_hook_func);
  ...
}

Next: , Previous: , Up: Setting up the transport layer   [Contents][Index]