UNPKG

otp-io

Version:

🕖 Typed library to work 2fa via Google Authenticator/Time-based TOTP/Hmac-based HOTP

65 lines (54 loc) • 1.75 kB
import { Hmac, HmacAlgorithm } from "./crypto/hmac"; import { hotp } from "./hotp"; import { SecretKey } from "./key"; import { getDefaultTOTPOptions, TOTPOptions } from "./totp.options"; const keyLengths = new Map<HmacAlgorithm, number>([ [HmacAlgorithm.SHA1, 20], [HmacAlgorithm.SHA256, 32], [HmacAlgorithm.SHA512, 64] ]); /** * * * @param {Uint8Array} buffer * @param {number} length * @return {Uint8Array} */ function pad(buffer: Uint8Array, length: number): Uint8Array { 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 */ export async function totp(hmac: Hmac, options: TOTPOptions): Promise<string> { const merged = { ...getDefaultTOTPOptions(), ...options }; const counter = Math.floor(merged.now.getTime() / 1000 / merged.stepSeconds); let bytes: Uint8Array = new Uint8Array(options.secret.bytes); if (merged.pad) { const length = keyLengths.get(merged.algorithm); /* istanbul ignore if: invalid algorithms are not used in tests */ 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 }); }