UNPKG

xotp

Version:

One-Time Password (HOTP/TOTP) library for Node.js, Deno and Bun, with support for Google Authenticator.

590 lines (547 loc) 22.1 kB
import require$$0 from 'node:crypto'; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var dist = {}; var totp = {}; var hotp = {}; var encoding$1 = {}; var uint8 = {}; var hasRequiredUint8; function requireUint8 () { if (hasRequiredUint8) return uint8; hasRequiredUint8 = 1; Object.defineProperty(uint8, "__esModule", { value: true }); uint8.uintEncode = uintEncode; uint8.uintDecode = uintDecode; uint8.uint64Encode = uint64Encode; uint8.uint64Decode = uint64Decode; function uintEncode(num) { const bytes = new Uint8Array(8).fill(0); for (let i = 7; i >= 0; i--) { bytes[i] = num & 0xff; // Shift 8 bits to the right and rounds down the result! // NOTE: Right shift (>>) operator only works on 32-bit // integers, but `num` could be an 8-byte integer! num = (num / Math.pow(2, 8)) | 0; } return bytes; } function uintDecode(bytes) { let num = 0; let arr = bytes; if (bytes instanceof Buffer) { arr = new Uint8Array(bytes); } for (let i = 0; i < arr.length; i++) { num = num * Math.pow(2, 8) + arr[i]; } return num; } function uint64Encode(num) { let int64Num = BigInt(num); const bytes = new Uint8Array(8); const dataView = new DataView(bytes.buffer); dataView.setBigInt64(0, int64Num); return bytes; } function uint64Decode(bytes) { let arr = bytes; if (bytes instanceof Buffer) { arr = new Uint8Array(bytes); } const dataView = new DataView(arr.buffer); return dataView.getBigUint64(0); } return uint8; } var base32 = {}; var hasRequiredBase32; function requireBase32 () { if (hasRequiredBase32) return base32; hasRequiredBase32 = 1; Object.defineProperty(base32, "__esModule", { value: true }); base32.base32Decode = base32.base32Encode = undefined; const formats = { RFC4648: { alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", padding: "=", }, RFC3548: { alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", padding: "=" }, RFC4648_HEX: { alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUV", padding: "=" }, CROCKFORD: { alphabet: "0123456789ABCDEFGHJKMNPQRSTVWXYZ", padding: "" }, }; const base32Encode = (arr, format = "RFC4648") => { const { padding, alphabet } = formats[format]; let bytes = 0b0; let left = 0; let base32 = ""; for (let i = 0; i < arr.byteLength; i++) { bytes = (bytes << 8) | arr[i]; left += 8; for (let j = 1; j <= left / 5; j++) { const charAt = (bytes >>> (left - j * 5)) & 0x1f; base32 += alphabet[charAt]; } left = left % 5; } if (left) base32 += alphabet[(bytes << (5 - left)) & 0x1f]; if (base32.length % 8 != 0) base32 += padding.repeat(8 - (base32.length % 8)); return base32; }; base32.base32Encode = base32Encode; const base32Decode = (base32Encoded, format = "RFC4648") => { const { padding, alphabet } = formats[format]; const removedPad = base32Encoded .replace(new RegExp(`${padding}+$`), "") .toUpperCase(); const bytesLen = ((removedPad.length * 5) / 8) | 0; const bytes = new Uint8Array(bytesLen); let binary = 0; let left = 0; let index = 0; for (let i = 0; i < removedPad.length; i++) { const charNum = alphabet.search(removedPad[i]); if (charNum === -1) throw new TypeError(`Invalid base32 character: ${removedPad[i]}`); binary = (binary << 5) | charNum; left += 5; if (left >= 8) { bytes[index++] = (binary >>> (left - 8)) & 0xff; left -= 8; } } return bytes; }; base32.base32Decode = base32Decode; return base32; } var hasRequiredEncoding$1; function requireEncoding$1 () { if (hasRequiredEncoding$1) return encoding$1; hasRequiredEncoding$1 = 1; (function (exports) { var __createBinding = (encoding$1 && encoding$1.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (encoding$1 && encoding$1.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); __exportStar(requireUint8(), exports); __exportStar(requireBase32(), exports); } (encoding$1)); return encoding$1; } var utils = {}; var hasRequiredUtils; function requireUtils () { if (hasRequiredUtils) return utils; hasRequiredUtils = 1; Object.defineProperty(utils, "__esModule", { value: true }); utils.padStart = undefined; const padStart = (str = "", length = str.length, padString = "") => { return padString.repeat(Math.max(length, str.length) - str.length) + str; }; utils.padStart = padStart; return utils; } var hasRequiredHotp; function requireHotp () { if (hasRequiredHotp) return hotp; hasRequiredHotp = 1; Object.defineProperty(hotp, "__esModule", { value: true }); hotp.HOTP = undefined; const node_crypto_1 = require$$0; const encoding_1 = requireEncoding$1(); const utils_1 = requireUtils(); class HOTP { constructor({ algorithm = this.defaults.algorithm, window = this.defaults.window, counter = this.defaults.counter, digits = this.defaults.digits, issuer = this.defaults.issuer, account = this.defaults.account, } = {}) { this.algorithm = this.defaults.algorithm; this.counter = this.defaults.counter; this.digits = this.defaults.digits; this.window = this.defaults.window; this.issuer = this.defaults.issuer; this.account = this.defaults.account; this.digits = digits; this.algorithm = algorithm; this.window = window; this.counter = counter; this.issuer = issuer; this.account = account; } get defaults() { return Object.freeze({ algorithm: "sha1", counter: 0, digits: 6, window: 1, issuer: "xotp", account: "", }); } generate({ secret, counter = ++this.counter, algorithm = this.algorithm, digits = this.digits, }) { const digest = (0, node_crypto_1.createHmac)(algorithm, secret.buffer) .update((0, encoding_1.uintEncode)(counter)) .digest(); const offset = digest[digest.byteLength - 1] & 0xf; const truncatedBinary = ((digest[offset] & 0x7f) << 24) | ((digest[offset + 1] & 0xff) << 16) | ((digest[offset + 2] & 0xff) << 8) | (digest[offset + 3] & 0xff); const token = truncatedBinary % Math.pow(10, digits); return (0, utils_1.padStart)(`${token}`, digits, "0"); } validate({ token, secret, counter = this.counter, algorithm = this.algorithm, digits = this.digits, window = this.window, }) { return (this.compare({ token, secret, counter, digits, algorithm, window, }) != null); } compare({ token, secret, counter = this.counter, digits = this.digits, algorithm = this.algorithm, window = this.window, }) { if (this.equals({ token, secret, counter, digits, algorithm })) return 0; for (let i = 1; i <= window; i++) { if (this.equals({ token, secret, counter: counter + i, digits, algorithm })) return i; if (this.equals({ token, secret, counter: counter - i, digits, algorithm })) return -i; } return null; } equals({ token, secret, counter = this.counter, algorithm = this.algorithm, digits = this.digits, }) { const generatedToken = this.generate({ secret, counter, algorithm, digits, }); return (0, node_crypto_1.timingSafeEqual)(Buffer.from(token), Buffer.from(generatedToken)); } keyUri({ secret, account, issuer = this.issuer, algorithm = this.algorithm, counter = this.counter, digits = this.digits, }) { const e = encodeURIComponent; const params = [ `secret=${e(secret.toString("base32").replace(/=+$/, ""))}`, `algorithm=${e(algorithm.toUpperCase())}`, `digits=${e(digits)}`, `counter=${e(counter)}`, ]; let label = account; if (issuer) { label = `${e(issuer)}:${e(label)}`; params.push(`issuer=${e(issuer)}`); } return `otpauth://hotp/${label}?${params.join("&")}`; } } hotp.HOTP = HOTP; return hotp; } var hasRequiredTotp; function requireTotp () { if (hasRequiredTotp) return totp; hasRequiredTotp = 1; var __classPrivateFieldSet = (totp && totp.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (totp && totp.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _TOTP_instances, _TOTP_hotp, _TOTP_calcHotpCounter; Object.defineProperty(totp, "__esModule", { value: true }); totp.TOTP = undefined; const hotp_1 = requireHotp(); class TOTP { constructor({ algorithm = this.defaults.algorithm, window = this.defaults.window, duration = this.defaults.duration, digits = this.defaults.digits, issuer = this.defaults.issuer, account = this.defaults.account, } = {}) { _TOTP_instances.add(this); this.algorithm = this.defaults.algorithm; this.digits = this.defaults.digits; this.window = this.defaults.window; this.duration = this.defaults.duration; this.issuer = this.defaults.issuer; this.account = this.defaults.account; _TOTP_hotp.set(this, undefined); this.algorithm = algorithm; this.digits = digits; this.window = window; this.duration = duration; this.issuer = issuer; this.account = account; __classPrivateFieldSet(this, _TOTP_hotp, new hotp_1.HOTP({ algorithm, window, digits }), "f"); } get defaults() { return Object.freeze({ algorithm: "sha1", duration: 30, digits: 6, window: 1, issuer: "xotp", account: "", }); } generate({ secret, timestamp = Date.now(), algorithm = this.algorithm, digits = this.digits, duration = this.duration, }) { return __classPrivateFieldGet(this, _TOTP_hotp, "f").generate({ secret, counter: __classPrivateFieldGet(this, _TOTP_instances, "m", _TOTP_calcHotpCounter).call(this, { timestamp, duration }), algorithm, digits, }); } validate({ token, secret, timestamp = Date.now(), algorithm = this.algorithm, digits = this.digits, duration = this.duration, window = this.window, }) { return __classPrivateFieldGet(this, _TOTP_hotp, "f").validate({ token, secret, counter: __classPrivateFieldGet(this, _TOTP_instances, "m", _TOTP_calcHotpCounter).call(this, { timestamp, duration }), algorithm, digits, window: window, }); } compare({ token, secret, timestamp = Date.now(), algorithm = this.algorithm, digits = this.digits, duration = this.duration, window = this.window, }) { return __classPrivateFieldGet(this, _TOTP_hotp, "f").compare({ token, secret, window, algorithm, digits, counter: __classPrivateFieldGet(this, _TOTP_instances, "m", _TOTP_calcHotpCounter).call(this, { timestamp, duration }), }); } equals({ token, secret, timestamp = Date.now(), algorithm = this.algorithm, digits = this.digits, duration = this.duration, }) { return __classPrivateFieldGet(this, _TOTP_hotp, "f").equals({ token, secret, algorithm, digits, counter: __classPrivateFieldGet(this, _TOTP_instances, "m", _TOTP_calcHotpCounter).call(this, { timestamp, duration }), }); } timeUsed({ timestamp = Date.now(), duration = this.duration, } = {}) { return ((timestamp / 1000) | 0) % duration; } timeRemaining({ timestamp = Date.now(), duration = this.duration, } = {}) { return duration - this.timeUsed({ timestamp, duration }); } keyUri({ secret, account, issuer = this.issuer, algorithm = this.algorithm, duration = this.duration, digits = this.digits, }) { const e = encodeURIComponent; const params = [ `secret=${e(secret.toString("base32").replace(/=+$/, ""))}`, `algorithm=${e(algorithm.toUpperCase())}`, `digits=${e(digits)}`, `period=${e(duration)}`, ]; let label = account; if (issuer) { label = `${e(issuer)}:${e(label)}`; params.push(`issuer=${e(issuer)}`); } return `otpauth://totp/${label}?${params.join("&")}`; } } totp.TOTP = TOTP; _TOTP_hotp = new WeakMap(), _TOTP_instances = new WeakSet(), _TOTP_calcHotpCounter = function _TOTP_calcHotpCounter({ timestamp, duration, }) { return (timestamp / 1000 / duration) | 0; }; return totp; } var secret = {}; var hasRequiredSecret; function requireSecret () { if (hasRequiredSecret) return secret; hasRequiredSecret = 1; var __classPrivateFieldGet = (secret && secret.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (secret && secret.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var _Secret_instances, _Secret_buffer, _Secret_getRecommendedSizeFor; Object.defineProperty(secret, "__esModule", { value: true }); secret.Secret = undefined; const node_crypto_1 = require$$0; const encoding_1 = requireEncoding$1(); class Secret { constructor({ data, algorithm, size = 160 / 8, } = {}) { _Secret_instances.add(this); _Secret_buffer.set(this, undefined); let buffer; if (data) { buffer = data; } else if (algorithm || size) { buffer = (0, node_crypto_1.randomBytes)((algorithm && __classPrivateFieldGet(this, _Secret_instances, "m", _Secret_getRecommendedSizeFor).call(this, algorithm)) || size); } else { throw new TypeError("Constructor arguments are not valid."); } __classPrivateFieldSet(this, _Secret_buffer, buffer, "f"); } get buffer() { return __classPrivateFieldGet(this, _Secret_buffer, "f"); } static for(algorithm) { return new Secret({ algorithm }); } static from(data, encoding = "utf-8") { if (typeof data == "string") { if (encoding == "base32") { const bytes = (0, encoding_1.base32Decode)(data); return new Secret({ data: Buffer.from(bytes) }); } return new Secret({ data: Buffer.from(data, encoding) }); } return new Secret({ data }); } toString(encoding = "base32") { if (encoding == "base32") return (0, encoding_1.base32Encode)(__classPrivateFieldGet(this, _Secret_buffer, "f")); return __classPrivateFieldGet(this, _Secret_buffer, "f").toString(encoding); } } secret.Secret = Secret; _Secret_buffer = new WeakMap(), _Secret_instances = new WeakSet(), _Secret_getRecommendedSizeFor = function _Secret_getRecommendedSizeFor(algorithm) { let size = 256 / 8; // As defined in RFC 2104, the length of secret key should not be less than // the digest size but the extra length would not significantly increase // the function strength. See https://tools.ietf.org/html/rfc4226 switch (algorithm) { case "sha1": size = 160 / 8; break; case "sha224": case "sha-512/224": case "sha3-224": size = 224 / 8; break; case "sha256": case "sha-512/256": case "sha3-256": size = 256 / 8; break; case "sha384": case "sha3-384": size = 384 / 8; break; case "sha512": case "sha3-512": size = 512 / 8; break; } return size; }; return secret; } var types = {}; var hotp_options = {}; var hasRequiredHotp_options; function requireHotp_options () { if (hasRequiredHotp_options) return hotp_options; hasRequiredHotp_options = 1; Object.defineProperty(hotp_options, "__esModule", { value: true }); return hotp_options; } var totp_options = {}; var hasRequiredTotp_options; function requireTotp_options () { if (hasRequiredTotp_options) return totp_options; hasRequiredTotp_options = 1; Object.defineProperty(totp_options, "__esModule", { value: true }); return totp_options; } var algorithms = {}; var hasRequiredAlgorithms; function requireAlgorithms () { if (hasRequiredAlgorithms) return algorithms; hasRequiredAlgorithms = 1; Object.defineProperty(algorithms, "__esModule", { value: true }); return algorithms; } var encoding = {}; var hasRequiredEncoding; function requireEncoding () { if (hasRequiredEncoding) return encoding; hasRequiredEncoding = 1; Object.defineProperty(encoding, "__esModule", { value: true }); return encoding; } var hasRequiredTypes; function requireTypes () { if (hasRequiredTypes) return types; hasRequiredTypes = 1; (function (exports) { var __createBinding = (types && types.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (types && types.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); __exportStar(requireHotp_options(), exports); __exportStar(requireTotp_options(), exports); __exportStar(requireAlgorithms(), exports); __exportStar(requireEncoding(), exports); } (types)); return types; } var hasRequiredDist; function requireDist () { if (hasRequiredDist) return dist; hasRequiredDist = 1; (function (exports) { var __createBinding = (dist && dist.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (dist && dist.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); __exportStar(requireTotp(), exports); __exportStar(requireHotp(), exports); __exportStar(requireSecret(), exports); __exportStar(requireTypes(), exports); } (dist)); return dist; } var distExports = requireDist(); var index = /*@__PURE__*/getDefaultExportFromCjs(distExports); export { index as default };