Simple two-factor authentication • Gated Logic • nevali.net

I’ve been pondering time-based two-factor authentication lately. I should probably state that I’m thinking about this in the context of an already fairly secure system: that is, the secondary authentication code (be it numeric or otherwise) would be passed over a link which is considered to be secure (e.g., SSL, or an established SSH connection). In other words, it would provide an additional means of verifying user identity, and nothing more.

Two-factor authentication systems generally work on the basis of a hash function (of some kind) being applied to the combination of a shared secret (the seed) which is unique to the authenticating user, and some kind of key which is either known or can be calculated by both parties. One approach used by one-time password systems is to use the previous output as the current input key. With time-based systems, the key is the current time (reduced to some kind of granularity, e.g., 30 seconds).

The key points are, therefore:

  • The seed is known to the authenticating user (or, more accurately, the authenticating user’s hardware token or software) and the server and does not change between authentication requests; the seed is generally at least 128 bits of random data
  • The key can be determined or calculated by both parties and does change (in OTP, it changes on every request; in time-based systems it changes on regular intervals)
  • Neither the key nor the seed can be determined from the output authentication code

It strikes me that a time-based software token would be a good candidate for an iPhone application (although, of course, it would only be useful for people whose servers ran the corresponding authentication software). Similar systems using J2ME applets for mobile phones have been trialled.

The algorithm used by the token would be:

o = f([seed] + [floor(timestamp / interval)])

Where f is a one-way hash function. On the server side, we would do:

for(n = 0, i = -floor(window / interval); i <= floor(window / interval); n++, i++)
{
    candiates[n] = f([seed] + [floor(timestamp / interval) - i])
}

Where window is the size of the clock skew window: this allows authentication to proceed (possibly with warnings) if the clock on the server and the clock on the token are out of sync with each other.

My envisaged mode of operation for the token would be that it would be protected by a PIN: upon connecting to a server and being challenged for an authentication code, the user would enter their PIN and the token would display the current code. After a specified timeout, or after being explicitly “locked”, the token would revert to an inert state.

One advantage of a PIN is that you can allow for a duress code: that is, an alternative seed used when the user indicates (by entering a different PIN) that they are being forced to authenticate themselves under duress. The server, when verifying the authentication code, would build a set of duress candidates (based upon the duress seed), as well as a set of normal candiates. If the code supplied matches a duress candidate, the server would allow authentication to proceed, but perform additional actions (such as invoking a script to send an emergency alert page).

I’m no crypto expert, however, and the problem I have is figuring out what to use for f—i.e., the one-way hash: the input size would likely be 160 bits (assuming a 128-bit seed), but the output can’t reasonably be any larger than 32 bits: it has to be entered reasonably quickly by the user, after all. Does anybody know of a 32-bit hash function for which knowing 32 of the 160 bits of input does not result in predictable collisions?