This post assumes you are familiar with Node.js and the crypto
module in Node.js, if you are not please see "What is Node.js" and/or "What Is the Crypto Module in Node.js?".
For those unaware of what RFC 4226 is, RFC 4226 is the HMAC--Based One-Time Password (HOTP) algorithm, which, stands as a fundamental method for generating one-time passwords used in two-factor authentication systems. Implementing this algorithm in Node.js can be achieved easily by levearging the built-in crypto
module. To quote RFC 4226:
...the HOTP algorithm is based on the
HMAC-SHA-1 algorithm (as specified in [RFC2104]) and applied to an
increasing counter value representing the message in the HMAC
computation.
Basically, the output of the HMAC-SHA-1 calculation is truncated to
obtain user-friendly values:
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
where Truncate represents the function that can convert an HMAC-SHA-1
value into an HOTP value. K and C represent the shared secret and
counter value; see [RFC4226] for detailed definitions.
To learn more about the HMAC-SHA-1 algorithm described in RFC 2104 please see "RFC 2104: HMAC: Keyed-Hashing for Message Authentication"
Understanding the HOTP Algorithm
The HOTP algorithm primarily utilizes HMAC-SHA-1, employing a secret key and a counter to produce a one-time password. The algorithm involves multiple steps, including:
- Generating an HMAC-SHA-1 hash by combining the secret key and counter.
- Extracting a dynamic binary value from the hash.
- Calculating a truncated numeric value from the binary, which constitutes the one-time password.
Implementing HOTP in Node.js
Step 1: The crypto
Module
Since the crypto
module provides the necessary tools to create HMAC-SHA-1 hashes and perform bitwise operations, implementing RFC 4226 is fairly trivial.
Firstly, we require the crypto
module.
const crypto = require('crypto');
Step 2: Implement the HMAC-SHA-1 Generator
Since we already imported crypto
via require
we can utilize the built-in functions in the crypto
module to create a new HMAC-SHA-1 hash based on a secret and a counter.
The second line in the code snippet above initializes the HMAC-SHA-1 hash based on the secret, while the next two lines update the value of the hash based o the value of the counter. Finally, the last line of the function processes the updates and returns the value of the updated hash.
Converting the HMAC-SHA-1 Hash to an OTP Code
Now that we can generate an HMAC-SAH-1 based hash, we can use that hash to create our HOTP code. This is the Truncate
functionality as described by the snippet taken from the RFC earlier.
In the function above, the second line of code calls our previously defined generateHmac
function and returns an HMAC-SHA-1 hash; however, now that we have the value of the hash, on the third line, we extract the offset for the starting point based on the last character of the hash. After extracting the offset we retrieve a dynamic binary value from a specific portion of the hash and apply a bitwise operation (& 0x7fffffff
) to ensure it is a positive integer, then after ensuring the value is positive, we return the numeric one-time password by taking the modulo (%
) of the binary value with 10 ** digits
and then padding it with leading zeros to reach the desired number of digits
(which defaults to 6).
How to use
const secret = 'YourSecretKeyHere'; // Replace with your secret key
const counter = 0; // Replace with the current counter value
const generatedHOTP = generateHOTP(secret, counter);
console.log('Generated HOTP:', generatedHOTP);
The above code shows how you would utilize the above functions to generate an HOTP code, when implementing the above code make sure to replace 'YourSecretKeyHere'
with your actual secret key and adjust the counter
value based on your current counter.
Implementing RFC 4226 to generate HOTP in Node.js relies on the core principles of cryptographic algorithms. This approach offers a deeper understanding of the algorithm's inner workings and fosters a foundation for implementing other cryptographic functionalities.
The full code for this post can be found below, and hopefully this article helped you learn about how to generate an HOTP in Node.js!
const crypto = require('crypto');
function generateHmac(secret, counter) {
const hmac = crypto.createHmac('sha1', secret);
hmac.update(Buffer.alloc(8, 0)); // Initializing the counter with zeros
hmac.update(Buffer.alloc(8).writeBigInt64BE(BigInt(counter), 0)); // Writing the counter value
return hmac.digest('hex');
}
function generateHOTP(secret, counter = 0, digits = 6) {
const hmacResult = generateHmac(secret, counter);
const offset = parseInt(hmacResult.slice(-1), 16) * 2;
const binary = (parseInt(hmacResult.slice(offset, offset + 8), 16) & 0x7fffffff).toString(10);
const otp = ('000000' + (parseInt(binary) % (10 ** digits))).slice(-digits);
return otp;
}
// Example usage
const secret = 'YourSecretKeyHere'; // Replace with your secret key
const counter = 0; // Replace with the current counter value
const generatedHOTP = generateHOTP(secret, counter);
console.log('Generated HOTP:', generatedHOTP);