Skip to content

Instantly share code, notes, and snippets.

@thatkid02
Last active April 17, 2024 17:28
Show Gist options
  • Save thatkid02/c9d2570b9abc917f4a3bf992fc0b4ef9 to your computer and use it in GitHub Desktop.
Save thatkid02/c9d2570b9abc917f4a3bf992fc0b4ef9 to your computer and use it in GitHub Desktop.
Implementing 2FA in javascript for 101
const crypto = require("crypto");
/**
* Convert decimal to hexadecimal.
* @param {number} s - Decimal number to convert.
* @returns {string} Hexadecimal representation of the decimal number.
*/
function dec2hex(s) {
return (s < 15.5 ? '0' : '') + Math.round(s).toString(16);
}
/**
* Convert hexadecimal to decimal.
* @param {string} s - Hexadecimal number to convert.
* @returns {number} Decimal representation of the hexadecimal number.
*/
function hex2dec(s) {
return parseInt(s, 16);
}
/**
* Convert base32 string to hexadecimal.
* @param {string} base32 - Base32 string to convert.
* @returns {string} Hexadecimal representation of the base32 string.
*/
function base32tohex(base32) {
// Base32 characters mapping
const base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
let bits = "";
let hex = "";
// Convert each character in base32 string to binary
for (let i = 0; i < base32.length; i++) {
const val = base32chars.indexOf(base32.charAt(i).toUpperCase());
bits += leftpad(val.toString(2), 5, '0');
}
// Convert binary to hexadecimal
for (let i = 0; i + 4 <= bits.length; i += 4) {
const chunk = bits.substr(i, 4);
hex = hex + parseInt(chunk, 2).toString(16);
}
return hex;
}
/**
* Left-pad a string with the specified character to the specified length.
* @param {string} str - String to pad.
* @param {number} len - Desired length after padding.
* @param {string} pad - Character used for padding.
* @returns {string} Left-padded string.
*/
function leftpad(str, len, pad) {
if (len + 1 >= str.length) {
str = Array(len + 1 - str.length).join(pad) + str;
}
return str;
}
/**
* Generate HMAC-SHA1 hash.
* @param {Buffer} key - Key used for hashing.
* @param {Buffer} data - Data to hash.
* @returns {string} Hexadecimal representation of the hash.
*/
function hmacSHA1(key, data) {
// Create HMAC-SHA1 hash using crypto module
const hashBuffer = crypto.createHmac('sha1', key).update(data).digest();
// Convert hash buffer to hexadecimal string
return Buffer.from(hashBuffer).toString('hex');
}
/**
* Generate One-Time Password (OTP) using HMAC-SHA1 algorithm.
* @param {string} secret - Secret key for OTP generation.
* @returns {string} 6-digit OTP.
*/
function generateOTP(secret) {
// Convert secret from base32 to hexadecimal
const key = base32tohex(secret);
// Get current time in seconds since epoch
const epoch = Math.round(new Date().getTime() / 1000.0);
// Convert time to hexadecimal
const time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0');
// Generate HMAC-SHA1 hash using key and time
const hmac = hmacSHA1(Buffer.from(key, 'hex'), Buffer.from(time, 'hex'));
// Extract offset and OTP value from HMAC hash
const offset = hex2dec(hmac.substring(hmac.length - 1));
const otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '';
return otp.substr(otp.length - 6, 6);
}
// Secret key for OTP generation
const secret = 'HMNDNFYJGHPE3PAX';
// Generate and print OTP
console.log(generateOTP(secret));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment