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
JavaScript
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 };