UNPKG

otpauth

Version:

One Time Password (HOTP/TOTP) library for Node.js and browsers

347 lines (285 loc) 7.21 kB
/** * An object containing some utilities. * @type {Object} */ export const Utils = { /** * UInt conversion. * @type {Object} */ uint: { /** * Converts an ArrayBuffer to an integer. * @param {ArrayBuffer} buf ArrayBuffer. * @returns {number} Integer. */ fromBuf: buf => { const arr = new Uint8Array(buf); let num = 0; for (let i = 0; i < arr.length; i++) { if (arr[i] !== 0) { num *= 256; num += arr[i]; } } return num; }, /** * Converts an integer to an ArrayBuffer. * @param {number} num Integer. * @returns {ArrayBuffer} ArrayBuffer. */ toBuf: num => { const buf = new ArrayBuffer(8); const arr = new Uint8Array(buf); let acc = num; for (let i = 7; i >= 0; i--) { if (acc === 0) break; arr[i] = acc & 255; acc -= arr[i]; acc /= 256; } return buf; } }, /** * Raw string conversion. * @type {Object} */ raw: { /** * Converts an ArrayBuffer to a string. * @param {ArrayBuffer} buf ArrayBuffer. * @returns {string} String. */ fromBuf: buf => { const arr = new Uint8Array(buf); let str = ''; for (let i = 0; i < arr.length; i++) { str += String.fromCharCode(arr[i]); } return str; }, /** * Converts a string to an ArrayBuffer. * @param {string} str String. * @returns {ArrayBuffer} ArrayBuffer. */ toBuf: str => { const buf = new ArrayBuffer(str.length); const arr = new Uint8Array(buf); for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt(i); } return buf; } }, /** * Base32 string conversion. * @type {Object} */ b32: { /** * RFC 4648 base32 alphabet without pad. * @type {string} */ alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', /** * Converts an ArrayBuffer to a base32 string (RFC 4648) * (https://github.com/LinusU/base32-encode). * @param {ArrayBuffer} buf ArrayBuffer. * @returns {string} Base32 string. */ fromBuf: buf => { const arr = new Uint8Array(buf); let bits = 0; let value = 0; let str = ''; for (let i = 0; i < arr.length; i++) { value = (value << 8) | arr[i]; bits += 8; while (bits >= 5) { str += Utils.b32.alphabet[(value >>> bits - 5) & 31]; bits -= 5; } } if (bits > 0) { str += Utils.b32.alphabet[(value << 5 - bits) & 31]; } return str; }, /** * Converts a base32 string to an ArrayBuffer (RFC 4648) * (https://github.com/LinusU/base32-decode). * @param {string} str Base32 string. * @returns {ArrayBuffer} ArrayBuffer. */ toBuf: str => { // Canonicalize to all upper case and remove padding if it exists. str = str.toUpperCase().replace(/=+$/, ''); const buf = new ArrayBuffer((str.length * 5) / 8 | 0); const arr = new Uint8Array(buf); let bits = 0; let value = 0; let index = 0; for (let i = 0; i < str.length; i++) { const idx = Utils.b32.alphabet.indexOf(str[i]); if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`); value = (value << 5) | idx; bits += 5; if (bits >= 8) { arr[index++] = (value >>> bits - 8) & 255; bits -= 8; } } return buf; } }, /** * Hexadecimal string conversion. * @type {Object} */ hex: { /** * Converts an ArrayBuffer to a hexadecimal string. * @param {ArrayBuffer} buf ArrayBuffer. * @returns {string} Hexadecimal string. */ fromBuf: buf => { const arr = new Uint8Array(buf); let str = ''; for (let i = 0; i < arr.length; i++) { const hex = arr[i].toString(16); str += hex.length === 2 ? hex : `0${hex}`; } return str.toUpperCase(); }, /** * Converts a hexadecimal string to an ArrayBuffer. * @param {string} str Hexadecimal string. * @returns {ArrayBuffer} ArrayBuffer. */ toBuf: str => { const buf = new ArrayBuffer(str.length / 2); const arr = new Uint8Array(buf); for (let i = 0, j = 0; i < arr.length; i += 1, j += 2) { arr[i] = parseInt(str.substr(j, 2), 16); } return buf; } }, /** * Pads a number with leading zeros. * @param {number|string} num Number. * @param {number} digits Digits. * @returns {string} Padded number. */ pad: (num, digits) => { let prefix = ''; let repeat = digits - String(num).length; while (repeat-- > 0) prefix += '0'; return `${prefix}${num}`; } }; /** * An object containing some utilities (for internal use only). * @private * @type {Object} */ export const InternalUtils = { /** * "globalThis" ponyfill * (https://mathiasbynens.be/notes/globalthis). * @type {Object} */ get globalThis() { let _globalThis; /* eslint-disable no-extend-native, no-restricted-globals, no-undef */ if (typeof globalThis === 'object') { _globalThis = globalThis; } else { Object.defineProperty(Object.prototype, '__magicalGlobalThis__', { get() { return this; }, configurable: true }); try { _globalThis = __magicalGlobalThis__; } finally { delete Object.prototype.__magicalGlobalThis__; } } if (typeof _globalThis === 'undefined') { // Still unable to determine "globalThis", fall back to a naive method. if (typeof self !== 'undefined') { _globalThis = self; } else if (typeof window !== 'undefined') { _globalThis = window; } else if (typeof global !== 'undefined') { _globalThis = global; } } /* eslint-enable */ Object.defineProperty(this, 'globalThis', { enumerable: true, value: _globalThis }); return this.globalThis; }, /** * "console" ponyfill. * @type {Object} */ get console() { const _console = {}; const methods = [ 'assert', 'clear', 'context', 'count', 'countReset', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeLog', 'timeStamp', 'trace', 'warn' ]; if (typeof InternalUtils.globalThis.console === 'object') { for (const method of methods) { _console[method] = typeof InternalUtils.globalThis.console[method] === 'function' ? InternalUtils.globalThis.console[method] : () => {}; } } else { for (const method of methods) { _console[method] = () => {}; } } Object.defineProperty(this, 'console', { enumerable: true, value: _console }); return this.console; }, /** * Detect if running in "Node.js". * @type {boolean} */ get isNode() { const _isNode = Object.prototype.toString.call(InternalUtils.globalThis.process) === '[object process]'; Object.defineProperty(this, 'isNode', { enumerable: true, value: _isNode }); return this.isNode; }, /** * Dynamically import "Node.js" modules. * (`eval` is used to prevent bundlers from including the module, * e.g., [webpack/webpack#8826](https://github.com/webpack/webpack/issues/8826)) * @type {Function} */ get nodeRequire() { const _nodeRequire = InternalUtils.isNode // eslint-disable-next-line no-eval ? eval('require') : () => {}; Object.defineProperty(this, 'nodeRequire', { enumerable: true, value: _nodeRequire }); return this.nodeRequire; } };