|
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)); |