Tokens

We've talked about authentication of humans as involving something you know, have, or are. The prototypical example of "have" is authentication tokens: the human is issued a machine called a token, that machine becomes an attribute of the human's identity, and authentication of the human reduces to authentication of the machine. So you can think of authentication tokens as being an intermediate step between authenticating humans and authenticating machines.

User convenience is an important issue. The machines being used as tokens should be small, light, require no maintenance (especially of a battery), and should cost very little. So issuing laptops isn't realistic. Rather, real-world examples include ATM cards, prox cards (like your Cornell id card), RSA SecurID tokens, and IronKey tokens.

Protocols for Local Authentication with Tokens

Goal: Authenticate a human Hu to a local system L using a token T.

Threat model: The adversary might read and replay messages but can't change them in the middle of a protocol execution. So we're considering only a limited form of man-in-the-middle (MitM) attacks. This matches well with real-world scenarios, in which wired keyboards or short-range radios (e.g., RFID) are used.

Enrollment: At enrollment, associate identifier id_T with identifier id_Hu.

The protocol we give next is an example of a challenge–response protocol. These are well-known from old spy movies: two agents who haven't met before authenticate each other by speaking an appropriate challenge and response, for example,

Here's our first attempt at a protocol. We assume that T is preprogrammed with a fixed response to a fixed challenge:

Authentication with Token, Attempt I

  1. Hu→T: I want to authenticate to L.
  2. T→L: id_T
  3. L: look up challenge question for id_T.
  4. L→T: challenge
  5. T→L: response
  6. L:
    • look up id_Hu and response associated with id_T
    • consider id_Hu authenticated if response is correct

Note that the human never declares her identity to either T or L. Instead, the token sends its identifier to L, and L is responsible for looking up the association between id_Hu and id_T.

Also note that, although the protocol is written such that L and T send messages directly to one another over a channel, Hu might in fact be involved in implementing that channel. For example, T might display a message on a screen, and Hu might type that message into L's keyboard.

Attempt I is vulnerable to replay attacks; an attacker need only eavesdrop and learn the response. Let's fix that by using unpredictable challenges and digital signatures.

Here's our second attempt at a protocol. We assume that T stores a signing key k_T for which L knows the verification key K_T:

Authentication with Token, Attempt II

  1. Hu→L: I want to authenticate with T.
  2. L: invent unpredictable nonce N_L.
  3. L→T: N_L
  4. T: compute s=Sign(N_L; k_T)
  5. T→L: id_T, s
  6. L:
    • lookup id_Hu and K_T associated with id_T
    • consider id_Hu authenticated if Ver(N_L; s; K_T)

Attempt II eliminates the replay vulnerability, because nonce N_L is (with high probability) different every execution.

Historically, tokens don't use Attempt II, in part because public-key crypto hasn't always been available on mobile devices. There's no security reason you couldn't use it now with modern devices—e.g., Android or USB flash drive. But for performance (e.g., time and power) reasons, less expensive crypto seems to be preferred. So shared-key crypto is what's used in many real-world tokens.

Here's our third attempt at a protocol. We assume that T and L share a secret MAC key k_T:

Authentication with Token, Attempt III

  1. Hu→L: I want to authenticate with T.
  2. L: invent unpredictable nonce N_L.
  3. L→T: N_L
  4. T: compute t=MAC(N_L; k_T)
  5. T→L: id_T, t
  6. L:
    • lookup id_Hu and k_T associated with id_T
    • consider id_Hu authenticated if t=MAC(N_L; k_T)

Did we just introduce a key distribution problem, in that we now have to arrange for T and L to share a key? Yes, but we already have to solve the problem of physically issuing tokens; piggy-backing installation of a shared key isn't hard.

A disadvantage of symmetric crypto is that L must now store the shared key k_T. Hashing and salting that key wouldn't work, because L actually needs to know the plaintext key. Alternative methods of storage can include using secure co-processors (e.g., TPMs, HSMs) to protect the secrecy of those keys on disk.

Two-factor authentication. There's a risk that T might be stolen. With the protocols above, a stolen T could be used by anyone to authenticate as id_Hu. We can use a second factor, something you know, as a countermeasure:

Two-factor Authentication with Token and PIN

  1. Hu→L: I want to authenticate with T.
  2. L: invent unpredictable nonce N_L.
  3. L→T: N_L
  4. T→Hu: Enter PIN on my keyboard.
  5. Hu→T: pin
  6. T: compute t=MAC(N_L, pin; k_T)
  7. T→L: id_T, t
  8. L:
    • lookup id_Hu, pin, and k_T associated with id_T
    • consider id_Hu authenticated if t=MAC(N_L, pin; k_T)

Unlike storage of keys in Attempt III, the PINs stored by L could be salted and hashed, as long as T can also be configured to store the salt. Then the tag would be computed as MAC(N_L, H(pin,salt); k_T), and L would store H(pin, salt) for each user.

Alternatively, the PIN could be stored locally on the token, and the token would authenticate the human, followed by the local system authenticating the token. In this design (as well as the original), the token must be careful not to enable online guessing attacks that reveal the PIN.

Protocol for Remote Authentication with Tokens

Goal: Authenticate a human Hu to a remote system S using a token T and local system L.

Threat model: On the channel between T and L, the adversary might read and replay messages but can't change them in the middle of a protocol execution. But on the channel between L and S, the adversary is Dolev-Yao.

In this new threat model, we have to worry about the channel between L and S. So, first, we can establish an SSL-secured channel between L and S. Then we can run an adaptation of the Two-Factor Authentication protocol. Any messages sent from T to S in the original protocol now are sent from T to L to S.

Remote Two-factor Authentication with Token and PIN

  1. Hu→L: I want to authenticate with T to S.
  2. L and S: establish SSL-secured channel.
  3. S: invent unpredictable nonce N_S.
  4. S→L→T: N_S
  5. T→Hu: Enter PIN on my keyboard.
  6. Hu→T: pin
  7. T: compute t=MAC(N_S, pin; k_T)
  8. T→L→S: id_T, t
  9. S:
    • lookup id_Hu, pin, and k_T associated with id_T
    • consider id_Hu authenticated if t=MAC(N_S, pin; k_T)

Case Study: RSA SecurID Tokens

Picture of RSA SecurID Token

The RSA SecurID token is a commercial product with an LCD display and internal clock, but with no means for input. It can compute hashes, has a secret stored on it, and is tamper resistant, such that it's hard to physically extract the secret. The LCD displays a value that changes every 60 seconds. Once that value is used for authentication, the server refuses to accept it again, so it is a one-time password (OTP).

Consider what follows to be a hypothetical protocol that could work with hardware similar to the SecurID token. There are token emulators you could investigate online if you want to know more. The main ideas behind this protocol are (i) to replace the unpredictable nonce used in Remote Two-factor Authentication by the time at the clocks of T and S, and (ii) to use L rather than T to input the PIN.

Hypothetical Protocol

  1. Hu→L: I want to authenticate as id_Hu to S.
  2. L and S: establish SSL-secured channel.
  3. L→Hu: Enter PIN and OTP on my keyboard.
  4. T→Hu: otp = MAC(current time at T, id_T; k_T).
  5. Hu→L: pin, otp
  6. L: compute h = H(pin,otp)
  7. L→S: id_Hu, h
  8. S:
    • lookup pin, id_T, and k_T associated id_Hu
    • consider id_Hu authenticated if h=H(pin, MAC(current time at S, id_T; k_T)).

One of the trickiest parts of implementing this protocol is that the clocks on T and S might not be perfectly synchronized (and messages might get delayed, etc.). So S might need to check a few different values of t in the final step, as well as store additional information about the last time T was successfully used to authenticate, and about the observed drift of T's clock; Schneider [section 5.2] provides some details.