Updated 2015-07-24 04:01:42 by aspect

The HOTP ([HMAC]-based One-Time Password) and TOTP (Time-based One-Time Password) algorithms form the basis of the two-factor authentication schemes (OATH) used by Google etc to provide additional security on login. Both work by computing a HMAC using a secret key shared between the client (usually a device or app on a smartphone) and the server and some moving factor that changes regularly. For HOTP, the moving factor is a simple counter that is incremented on each call, for TOTP the moving factor is the current time (divided into some large time-step, such as 30 seconds). The client computes the current OTP using the shared secret and the current moving factor and sends it to the server. The server performs the same computation and checks that it matches what the client sent - if so then the client is authenticated (assuming any additional checks, such as a "first-factor" of username/password have been satisfied), if not then authentication is denied or additional measures must be satisfied.

The following procedures implement the basic algorithms (based on the sha1 hmac functionality, not yet implemented sha2 variants). I have not implemented the resynchronisation aspects that are described in the RFCs - this is left as an exercise for the reader!
package require Tcl 8.5
package require sha1 2.0

# hotp secret movingFactor ?length? --
#
#    Computes a HOTP for the given parameters:
#      - secret - the hex-encoded shared secret
#      - movingFactor - the moving factor (a 64-bit integer, typically a counter that is incremented on each call)
#      - length - the length of HOTP to compute (defaults to 6)
proc hotp {secret movingFactor {length 6}} {
    set movingFactor [binary format W $movingFactor]
    set hash [sha1::hmac -bin -key [binary decode hex $secret] $movingFactor]

    # Use last 4 bits to determine offset into hash to take code from
    binary scan [string index $hash end] c lastByte
    set offset [expr {$lastByte & 0x0F}]

    # Read code as a big-endian 32-bit integer
    binary scan [string range $hash $offset [incr offset 3]] I code

    # Truncate the code into correct range and left-pad with zeros
    format %0${length}.${length}d [expr {$code % (10 ** $length)}]
}

# totp secret ?interval? ?length? --
#
#    Computes a TOTP for the given parameters:
#      - secret - the hex-encoded shared secret
#      - interval - the size of TOTP time-steps in seconds (defaults to 30 seconds)
#      - length - the length of the OTP to compute (defaults to 6)
proc totp {secret {interval 30} {length 6}} {
    hotp $secret [expr {[clock seconds] / $interval}] $length
}

A slightly different implementation using TclOO exists on Rosetta Code - TOTP, with an example of computing TOTP compatible with Google Authenticator.