What is TOTP? (Time-based one-time password)
Internet users regularly have to enter passwords – for example, when signing into social media platforms, shopping online, or using internet banking. Passwords prevent third parties from accessing sensitive data. However, many users are not sufficiently diligent about online security: professionals can crack simple passwords in a matter of seconds. Other people create good passwords, but fail to store them safely, thus leaving a door open to criminals. In addition, weaknesses in the services which users subscribe to are no small matter either. If such services do not properly secure passwords, the data of thousands of users can be at risk.
One way of minimising the risk is to use two-factor authentication or multi-factor authentication. Instead of using just a password, at least one other means of authentication is required. Users of this technology obtain this additional authentication factor via their smartphone or a special hardware device (called a “token”). The common point between most of these additional factors is that they are unique and valid only for a short time – in other words, a time-based one-time password is generated. Let’s take a look at how that works.
Why do we need a TOTP?
Conventional passwords – however strong the user makes them – have a disadvantage: if somebody else knows the character string, security is no longer guaranteed. One solution would be to change passwords regularly, but even the most exemplary users do not do this every hour. The solution is a TOTP: a password which is only valid for a brief time, after which it expires. The Internet Engineering Task Force (IETF) published the one-time password algorithm in 2011 in RFC 6238 to facilitate greater online security.
One-time passwords are mostly used as part of multi-factor authentication, a security system which requires users signing into a web service to first enter their personal, static password, and then a time-limited password generated especially for this sign-in. The user receives the second password via an app or a special hardware token.
Once the one-time password has been used, or if it has not been used after a specified time, it expires. It is therefore very difficult for criminals to obtain the second factor, as even if they know the static password, it's very hard for them to obtain the TOTP too, especially as they have barely any time to crack it.
How does the time-based one-time password algorithm work?
The TOTP is based on a hash function, which is a cryptographic procedure whereby a secret key and a time stamp are combined to form an encrypted character string. Both the user and the server know the secret key. The time stamp is given in Unix time.
Unix time is a value corresponding to the number of seconds that have passed since January 1, 1970.
TOTP is in fact a further development of HOTP, which stands for HMAC-based one-time password. Like HOTP, TOTP is based on the HMAC procedure – the hash operation in the background. Both the user’s device and the server generate a hash value by combining the secret key with a counter. The two values are identical, which is how the authentication works.
The hash function itself is not defined; in practice SHA-1 is often used (including by Google Authenticator, for example). SHA-1 generates a 160-bit hash value. For convenience, this value is truncated using a compression function. The final result is a short number (six digits for example) which the user can easily use to sign in to the web service.
The secret key is embedded in a token in the device, meaning that often even the user doesn’t know it. If this is not the case, the secret key must be stored securely, ideally offline, or even printed and kept in a safe place for example. If the user loses the secret key, they will no longer be able to sign in to the service.
For the second part of the function, HOTP uses a counter, and this is shared by the server and the user. The problem with this is that the generated code remains valid until it is used. TOTP restricts this: the generated code can only be used within a defined time frame. How does this work?
For the time-based one-time password algorithm, there are three important formulas:
TOTP = HOTP(SecretKey,CurrentTime)
This basic formula simply defines that the TOTP is a HOTP procedure with two parameters – SecretKey and CurrentTime:
- SecretKey: Randomly generated password, known to both the server and the client
- CurrentTime: Current time in Unix time
However, this time value changes every second, which doesn’t leave the user long enough to enter the generated code. In other words, one second later, the TOTP is no longer valid, because the server has already generated a new hash value. A further formula is therefore required:
CurrentTime = floor((unixtime(now) – unixtime(T0))/T1)
The CurrentTime parameter is defined as follows:
- unixtime(now): Current time in Unix time
- unixtime(T0): Unix time at T0, the point from which the time steps are counted – in most cases midnight on January 1, 1970 (=0)
- T1: The period for which the TOTP will be valid (usually 30 seconds)
- floor: Rounding function to round the calculated value down to a whole number
In theory, T0 can take any value, not necessarily 0. The important thing is that the client and the server select the same value.
The effect of dividing and rounding is that the result changes at defined intervals.
Next, the generated hash value is truncated to make it more user-friendly.
Result = TOTPmod10d
The modulo operation generates a checksum:
- mod 10: Modulo with divisor = 10
- d: Desired number of digits of the TOTP
In other words, the base (10) is raised to the desired number of digits, then the TOTP is divided by this value and the remainder is extracted.
Example of a TOTP calculation
Let us assume that we want to generate a TOTP which is valid for 30 seconds. We can start by calculating the CurrentTime and determining how long the validity will be guaranteed for. For the unixtime(now), let us take 1548322860, i.e. 10:41 a.m. on January 24, 2019. If we divide this value by 30, we get exactly 51610762. Because this is already a whole number, rounding would give the same result. However, if we set the CurrentTime 15 seconds later (i.e. 1548322875), after dividing, the result is 51610762.5. After rounding, the value is 51610762, as before. The CurrentTime therefore remains the same. The following table shows that a new value is only generated every 30 seconds.
unixtime(now) | unixtime(now)/30 | floor(unixtime(now)/30) |
---|---|---|
1548322857 | 51610761.9000 | 51610761 |
1548322858 | 51610761.9333 | 51610761 |
1548322859 | 51610761.9667 | 51610761 |
1548322860 | 51610762.0000 | 51610762 |
1548322861 | 51610762.0333 | 51610762 |
1548322862 | 51610762.0667 | 51610762 |
1548322863 | 51610762.1000 | 51610762 |
1548322864 | 51610762.1333 | 51610762 |
1548322865 | 51610762.1667 | 51610762 |
1548322866 | 51610762.2000 | 51610762 |
1548322867 | 51610762.2333 | 51610762 |
1548322868 | 51610762.2667 | 51610762 |
1548322869 | 51610762.3000 | 51610762 |
1548322870 | 51610762.3333 | 51610762 |
1548322871 | 51610762.3667 | 51610762 |
1548322872 | 51610762.4000 | 51610762 |
1548322873 | 51610762.4333 | 51610762 |
1548322874 | 51610762.4667 | 51610762 |
1548322875 | 51610762.5000 | 51610762 |
1548322876 | 51610762.5333 | 51610762 |
1548322877 | 51610762.5667 | 51610762 |
1548322878 | 51610762.6000 | 51610762 |
1548322879 | 51610762.6333 | 51610762 |
1548322880 | 51610762.6667 | 51610762 |
1548322881 | 51610762.7000 | 51610762 |
1548322882 | 51610762.7333 | 51610762 |
1548322883 | 51610762.7667 | 51610762 |
1548322884 | 51610762.8000 | 51610762 |
1548322885 | 51610762.8333 | 51610762 |
1548322886 | 51610762.8667 | 51610762 |
1548322887 | 51610762.9000 | 51610762 |
1548322888 | 51610762.9333 | 51610762 |
1548322889 | 51610762.9667 | 51610762 |
1548322890 | 51610763.0000 | 51610763 |
1548322891 | 51610763.0333 | 51610763 |
Our CurrentTime is therefore established (51610762). We now use a password generator to obtain the SecretKey: >cHSB_UQ#O5m;~b
HMAC (with SHA-1) combines the secret key and the current time to produce a hash value (in hexadecimal format): c0 62 37 94 dd 37 7a 3a f0 91 22 08 1f 21 6f 9b 17 4b 17 45. This 160-bit (20 byte) value is truncated to 31 bits using what is known as dynamic truncation. This involves performing the following steps: First, the last 4 bits are considered. In our example, this is the number 0x5, which can also be written as 5 in decimal notation. This gives the offset value to be used for dynamic truncation, and means that starting from the byte with index 5, we must extract four bytes (read from the left, starting at 0): 0x377a3af0. In this example our value already starts with a bit equal to 0. If this is not the case, it must be changed accordingly. The 31-bit value here is thus: 0x377a3af0, or 930757360.
To reduce this 9-digit string to 6 digits, we use a modulo operation, padding the result with leading zeros if necessary: 930757360 mod (106) = 757360. This is now the TOTP, which is valid for 30 seconds. In combination with another factor, this makes the sign-in procedure fairly secure.