How Time-based OTP (TOTP) works
Sep 10, 2023
Photo by Wil Stewart on Unsplash
The adoption of two-factor authentication (2FA) has been steadily growing over the past three years. While there are several 2FA methods, OTPs account for most of them. However, a cybercriminal can still access an account (or resource) protected by an OTP by exploiting vulnerabilities in the SS7 protocol. The SS7 protocol was developed during the 1970s in a world of landlines and public-switched telephony. A cybercriminal can exploit the vulnerabilities in the SS7 protocol to gain access to SMS and call records. Besides, there are other methods, such as SIM-swapping and SMShing, that they may use to access OTPs. Time-based OTPs (TOTPs) are a more secure method of 2FA because they do not rely on SMS.
TOTPs generated on the user end
When a Service (such as GMail) asks for a TOTP, an authenticator is used to generate the TOTP. Authenticators can be mobile apps or physical devices. An authenticator app generates TOTPs and changes them every few seconds. The Service verifies the TOTP when the user sends it as part of the authentication flow. Access to the account or resource (like email) is granted if it's valid. When using an (SMS-based) OTP, the Service generates the OTP, so sending it back confirms the user's identity to the Service. However, a TOTP is generated on the user's device or app, so how does the Service determine its validity?
Generating TOTPs on the server end
The secret is the Service also generates the TOTP. Access is granted if the service-generated and user-provided TOTPs match; otherwise, access is denied.
Shared Keys and Algorithms
The following diagram illustrates the authentication flow.
The Authenticator asks for a shared key associated with the user account registered with the Service. The shared key is a random string of 16-32 characters. Typically, the Service shares the key as a QR code that the Authenticator can scan.
When the user uses the Authenticator to generate a TOTP, a timestamp and shared key create a cryptographic hash. The timestamp is the current timestamp accounting for the change interval. Typically, the change interval is 60 or 30 seconds. At any point between an interval, the timestamp used for the hash creation is the timestamp at the beginning of the interval.
For example, in the following diagram, when a TOTP is generated between t0
and t1
, t0
is used for the timestamp; if generated between t1
and t2
, t1
is used for the timestamp, and so on.
The beginning of the interval may be calculated as follows.
// get the number of seconds in the change interval
final var interval = changeInterval.getSeconds();
// get the current time in terms of seconds elapsed
// since Epoch (Jan 1, 1970 00:00:00 UTC)
final var ldt = LocalDateTime.now();
final var ts = ldt.toEpochSecond(ZoneOffset.UTC);
// calculate the remainder from dividing the current time with the interval
final var remainder = ts % interval;
// subtract the remainder from the current timestamp to get the timestamp
// at the beginning of the change interval
final var tsb = ts - remainder;
After that, a numeric value is randomly generated using the cryptographic hash as a seed. Only the first few bits of the hash may be used to generate a numeric value with six digits which is the usual length of a TOTP.
The same flow happens on the Service end when it needs to validate the TOTP sent by the user. The Service also needs to know the change interval, which the Authenticator app can share as part of the initial key-sharing exchange.
Correcting for clock drift
One potential issue with this flow is the mismatch of the time interval used in the Authenticator versus the Service. This mismatch can happen due to clock drift and network delays. When the Authenticator generates a TOTP very close to an interval boundary, there's a chance that its verification at the Service end happens in the next interval. Typically, Services solve for this drift by calculating the TOTP at the current and preceding intervals (or only half the preceding interval).
Understanding with a demo
The demo can be accessed from the GitHub repository. It has three modules: client, server, and shared. Refer to the README
to run the demo.
The client emulates the Authenticator. It registers with the server first. The server uses a fixed string as the shared secret for a demo purpose only. The algorithms for generating the timestamp at the interval boundary, the cryptographic hash value, and the TOTP numeric value are part of the shared module. The client and server modules use this shared module to calculate TOTPs using the same strategy in two independent runtimes.
Service Components
The TOTP Server and related components implement the Service.
Authenticator Components
The TOTP Client and related components implement the Authenticator functions.
Related Posts
Effective Dependency Inversion
Dec 25 2022
Can you spot the issues in the following code snippet.
Flux-CD Pattern for AWS CDK8s Services
Jul 9 2023
AWS Cloud Development Kit for Kubernetes called generates Kubernetes manifest files for Kubernetes () deployments and services.
How Headless CMS work
Jun 25 2023
Headless CMSs came about because it is hard to build a single platform that content writers like using and software developers like…
JAM Stack
Nov 29 2020
In a previous post on how this blog works, I referred to Gatsby and GraphQL.
Password-less Web Login with a Mobile App
Apr 17 2022
If you have used Whatsapp web, you have experienced the use case explored in this post.
The First Post
Apr 20 2019
Just like fashion, technology tends to go in circles, and static web sites are rising in popularity for content.