otp-io
Version:
🕖 Typed library to work 2fa via Google Authenticator/Time-based TOTP/Hmac-based HOTP
49 lines • 1.65 kB
JavaScript
import {HmacAlgorithm}from'./crypto/hmac.mjs';import {hotp}from'./hotp.mjs';import {SecretKey}from'./key.mjs';import {getDefaultTOTPOptions}from'./totp.options.mjs';const keyLengths = new Map([
[HmacAlgorithm.SHA1, 20],
[HmacAlgorithm.SHA256, 32],
[HmacAlgorithm.SHA512, 64]
]);
/**
*
*
* @param {Uint8Array} buffer
* @param {number} length
* @return {Uint8Array}
*/
function pad(buffer, length) {
const factor = Math.ceil(length / buffer.length);
const double = new Uint8Array(buffer.length * factor);
for (let index = 0; index < factor; index++) {
double.set(buffer, buffer.length * index);
}
return double.slice(0, length);
}
/**
* Generates TOTP code from secret key and options
*
* @export
* @param {Hmac} hmac
* @param {TOTPOptions} options
* @return {Promise<string>}
* @throws {Error} if HMAC algorithm is invalid. Use {@link HmacAlgorithm} to avoid this
*/
async function totp(hmac, options) {
const merged = Object.assign(Object.assign({}, getDefaultTOTPOptions()), options);
const counter = Math.floor(merged.now.getTime() / 1000 / merged.stepSeconds);
let bytes = new Uint8Array(options.secret.bytes);
if (merged.pad) {
const length = keyLengths.get(merged.algorithm);
if (length === undefined) {
throw new Error(`Invalid hmac algorithm: "${merged.algorithm}"`);
}
if (bytes.length < length) {
bytes = pad(bytes, length);
}
}
return await hotp(hmac, {
secret: new SecretKey(bytes),
algorithm: merged.algorithm,
digits: merged.digits,
counter
});
}export{totp};