How does the Time-Based One-Time Password (TOTP) algorithm work?

Cybersecurity has always been a weak point for me and after setting up Authy for the n-th time, it seemed as good a time as any to take a deep dive and figure out exactly how these time-based one-time password algorithms worked.

Examples of different time-based one-time password algorithm implementations

Basic Overview

At the highest level, the passcodes you see on these apps are created using a TOTP (time-based one-time password) algorithm. The passcode is generated by combining a secret key from the service you’re authenticating with and the current time. That’s why the codes always expire after a few seconds. 

If you’ve set up these apps yourselves, you’ll probably remember having to scan a QR code somewhere along the way. That was the transfer of the secret key to your device’s secure keychain storage. This algorithm is agnostic of your SIM card, cellular network provider, or the make/model of your phone which makes it preferable to texting the user an authentication code. The algorithm’s only considerations are the current time and the shared secret key. 

Whenever you create a new account on a service that supports TOTP, they’ll generate a unique secret code for your user account. Then, the next time you’re logging in, the website will run the same algorithm the client runs to generate a passcode. If the passcode from the client matches the one generated by the server, the user is authenticated and the two-factor authentication process is complete.

Algorithm Overview

I’ve always found it easier to understand concepts like this by looking directly at the code. Let’s see what we’d need to do to implement a TOTP service from scratch. 

TOTP is built off of a foundational algorithm called the HMAC-based One-time Password algorithm (HOTP), which we’ll need to understand first. 

HMAC (hash-based message authentication code) is simply an algorithm that uses

  • a cryptographic hash function (commonly SHA1)
  • a secret cryptographic key
  • a counter property (used to keep the client and server TOTP processes in sync) 

to generate a unique time based authentication code. 

If the distinction between TOTP and HOTP/HMAC isn’t totally clear yet, don’t worry. The main difference between TOTP and HOTP implementations involve the counter property.

HOTP’s counter is generally event-driven – for example, a button press would change the counter value.

An example device implementing the HOTP algorithm

Instead, in TOTP, the counter value is set based on the current timestamp.

Let’s take a look at the end to end flow.

First, we’ll create a counter from the timestamp. To avoid any additional complexity, we’ll just use time since epoch which is agnostic of timezones. 

We need the number of Tx intervals to provide as input into the HOTP algorithm.

Simply put, the result of this calculation is to figure out how many Tx intervals away from the Unix epoch the user is.

Full TOTP Flow

Implementation

Here’s the Swift code thus far: 

 let secretKey = "digitalbunker"
 let counter = "53283545"
 let hmacResult: String = secretKey.hmac(algorithm: .SHA1,  counter: counter)

Now, hmacResult is equal to the following hashed value 1OyZRjbfhe+8womld8xldXo5V9M=.

Next, Let’s convert this hashed value to a hexadecimal string: 

let data = Data(hmacResult.utf8)
let hexString = data.map{String(format:"%02x ", $0)}

hexString is now equal to 31 4f 79 5a 52 6a 62 66 68 65 2b 38 77 6f 6d 6c 64 38 78 6c 64 58 6f 35 56 39 4d 3d.

This isn’t something we can ask the user to type in every single time they need to authenticate. Let’s see if we can find an easier way of representing this – a 6 digit code would be a great user-friendly alternative.

We’ll convert this hashed value to a shorter code through a process called dynamic truncation. 

Dynamic truncation involves taking the lower 4 bits of the last byte of the hexadecimal string and using that as an offset into the string to help us create a 6 digit code while still remaining secure. 

The last byte is 3d which means the last 4 bits belong to the hexadecimal value for d (decimal 13).

Starting at an offset of 13, we’ll grab the next 4 bytes: 

31 4f 79 5a 52 6a 62 66 68 65 2b 38 77 6f 6d 6c 64 38 78 6c 64 58 6f 35 56 39 4d 3d

These bit operations are all part of the dynamic truncation process. We’re doing all of these manipulations to find a 6 digit code that is still as secure as possible. 

Now, we’ll convert 0x6f6d6c64 to decimal giving us 1869442148. Since we just need a 6 digit code, we can take the last 6 digits 442148 and return that to the user. 

Now, the server will do the exact same sequence of steps. More accurately, it’ll check the current 30-second interval, the previous one, and the next one to account for clock drift. If the server’s code matches the code the user entered, the user is authenticated.

Closing Thoughts

TOPT and HOPT are both well-respected algorithms, but it’s important to mention the limitations. TOTP values can be phished like other passwords, but the attacker would have to be stealing the credentials in near real-time.

If this is a concern, you can shorten the TTL of the code. Standard TTL’s of TOTP codes can range from 10 – 30 seconds to account for latency (both client and server-side), unsynchronized clocks, and for user usability – feel free to tweak the TTL to suit your needs. However, if the attacker was able to find the shared secret key, they’d be able to create valid TOTP codes at will. 

Hope you’ve enjoyed this closer look at the TOTP algorithm and its nuances! If you like contact like this, please join our mailing list. If there’s any article requests or comments, please feel free to reach out.