Enhancing Security with Two-Factor Authentication: An Introduction to TOTP and HOTP
This article will discuss 2FA options and take a closer look at implementations of the commonly used TOTP and HOTP protocols.
Join the DZone community and get the full member experience.
Join For FreeOne of the most popular method of authentication remains the password. In a previous article, we discussed the proper implementation of password authentication. However, relying solely on a password as the means of authentication is no longer sufficient, especially for critical accounts, such as bank accounts or personal email accounts, that serve as the point of access to other services.
Two-factor authentication (2FA) significantly enhances account security by adding an extra step in the login process. In this article, we will discuss 2FA options and take a closer look at implementations of the commonly used time-based one-time password (TOTP) and a password and HMAC-based one-time password (HOTP) protocols.
Two-Factor Authentication
When it comes to 2FA, it involves an additional step in user authentication. This could be achieved through various means, including:
- A one-time password (OTP) is sent to the user's phone via cellular communication (SMS).
- An OTP is sent to the user's email (or through a magic link).
- A push notification to the user's mobile application, which may include a code or the need to confirm the login within the application.
- The use of TOTP/HOTP protocols.
If a malicious party has obtained your password through theft or guessing, they will not be able to access your account without confirming the second factor.
Let's take a closer look at TOTP/HOTP protocols and their implementation.
TOTP/HOTP
The main distinguishing feature of these protocols is that the server does not need to send an OTP to the user's phone or email. The authentication process is illustrated in the following diagram:
After the user logs in with their username and password, one is prompted to enter an OTP. The user then opens an OTP-generating application, such as Google Authenticator, and enters the generated code.
To set up 2FA on the server, a secret key is generated and transferred to the user's OTP-generating application. The process of passing the secret key can be as follows: the user either scans the QR code or enters the secret key manually. Based on this key, we will generate the one-time passwords. To keep the password constantly changing, we need to enter some variable and use it in the algorithm.
HOTP: HMAC-Based One-Time Password Algorithm
The algorithm is based on HMAC (SHA-1). It uses a counter as a variable:
code = HMAC-SHA-1(secretKey, counter)
The result of the execution is quite a long value, so the code is reduced to 6-8 characters for the user's convenience. If the server and the client know the secret key and increment the counter equally at each user input, the resulting code value will be the same, and an OTP will change at each input. Thus, if an attacker intercepts the OTP code, he won't be able to use it again.
This algorithm is not very handy, but it was often used in the 2000s when there were no convenient applications. For example, a list of generated passwords provided by the bank was used to confirm access or transactions on a user's bank account. After the user used all the passwords from the list, he had to visit the bank office to get a new list.
Time-based One-Time Password Algorithm (TOTP)
This algorithm is based on the previous one and uses a time step as a variable. As a rule, the step is chosen with a duration of 30 seconds, and an OTP is automatically generated in the application every 30 seconds.
code = HMAC-SHA-1(secretKey, currentTime/step)
If we take the current Unix-time value and divide it by 30 sec, we get the counter value. If the clocks on the server and the client are synchronized, the value of the code will be the same.
To prevent errors when the time is out of sync when you check the code on the backend, you can also successfully authenticate the user if he sent the code from an adjacent interval.
Implementation
According to the requirements of RFC4226, we need to generate a key length of 160 bits:
SecureRandom random = new SecureRandom();
byte[] secretKeyBytes = new byte[20];
random.nextBytes(secretKeyBytes);
String secretKey = new Base32().encodeToString(secretKeyBytes);
We need to save the resulting value for the account and generate a link for the user:
otpauth://totp/{LOGIN}?secret={BASE32(SECRET_BYTES)}&issuer={ISSUER}
Here ISSUER
, is a string value indicating the provider or service this account is associated with (URL-encoded).
For example:
otpauth://totp/alice?secret=LLZYIV5MTMMNI4H6CIPB6IBAQW34NRDG&issuer=Example
It is possible to generate a QR code from this link and show the user:
Scan it using Google Authenticator:
From now on, an OTP will be generated on the user's device every 30 seconds. To check on the server, we need to similarly generate an OTP using the saved secret key and a timestamp.
private static final int TOTP_DEFAULT_TIME_STEP = 30;
private static final int TOTP_DEFAULT_CODE_DIGITS = 6;
private static final String secretKey = "LLZYIV5MTMMNI4H6CIPB6IBAQW34NRDG";
...
long timeStep = System.currentTimeMillis() / 1000 / TOTP_DEFAULT_TIME_STEP;
//convert long to byte array
byte[] timeStepBytes = new byte[8];
for (int i = 7; i >= 0; i--) {
timeStepBytes[i] = (byte) (timeStep & 0xFF);
timeStep >>= 8;
}
//Compute HMAC
byte[] secretKeyBytes = new Base32().decode(secretKey);
SecretKeySpec signingKey = new SecretKeySpec(secretKeyBytes, "RAW");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] hmac = mac.doFinal(timeStepBytes);
The resulting hash value is much larger than the six characters we need. We need to truncate this value. The truncation algorithm is described in RFC4226.
int offset = hmac[hmac.length - 1] & 0xF;
int binary =
((hmac[offset] & 0x7F) << 24)
| ((hmac[offset + 1] & 0xFF) << 16)
| ((hmac[offset + 2] & 0xFF) << 8)
| (hmac[offset + 3] & 0xFF);
String totp = Integer.toString(binary % (int) Math.pow(10, TOTP_DEFAULT_CODE_DIGITS));
if (totp.length() < TOTP_DEFAULT_CODE_DIGITS) {
totp = "0".repeat(TOTP_DEFAULT_CODE_DIGITS - totp.length()) + totp;
}
The resulting value should be the same value generated by the mobile app.
Conclusion
Two-factor authentication (2FA) is a crucial aspect of enhancing the security of any system. TOTP and HOTP are two of the most commonly used 2FA algorithms, offering an added security layer to the traditional username and password approach. Implementing 2FA using TOTP or HOTP can significantly enhance the security of your applications and protect against the potential risks posed by unauthorized access.
Opinions expressed by DZone contributors are their own.
Comments