wip:milestone 0 fixes
Some checks failed
CI/CD Pipeline / unit-tests (push) Failing after 1m16s
CI/CD Pipeline / integration-tests (push) Failing after 2m32s
CI/CD Pipeline / lint (push) Successful in 5m22s
CI/CD Pipeline / e2e-tests (push) Has been skipped
CI/CD Pipeline / build (push) Has been skipped

This commit is contained in:
2026-03-15 12:35:42 +02:00
parent 6708cf28a7
commit cffdf8af86
61266 changed files with 4511646 additions and 1938 deletions

View File

@@ -0,0 +1,21 @@
# The MIT License (MIT)
Copyright © Héctor Molinero Fernández
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,150 @@
[![Last version](https://img.shields.io/github/v/tag/hectorm/otpauth?label=version)](https://github.com/hectorm/otpauth/tags)
[![npm downloads](https://img.shields.io/npm/dm/otpauth?label=npm%20downloads)](https://www.npmjs.com/package/otpauth)
<p align="center">
<img alt="OTPAuth" src="./resources/logo/OTPAuth-Color-Reduced.svg" height="192" />
</p>
# OTPAuth
One Time Password library for Node.js, Deno, Bun and browsers.
It supports the generation and validation of
HMAC-Based One-Time Passwords (HOTP) as specified in [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226) and
Time-Based One-Time Passwords (TOTP) as specified in [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238).
Frequently used in Multi-Factor Authentication (MFA) / Two-Factor Authentication (2FA) systems.
> [!TIP]
> You can try the library with the demo application available at [otpauth.molinero.dev](https://otpauth.molinero.dev).
>
> If you wish to interact with the library in your browser console, the following snippet can be used:
>
> ```javascript
> const OTPAuth = await import("otpauth");
> ```
## Usage
This section presents an overview of the most common usage patterns, along with some security recommendations.
### [Node.js / Bun](https://www.npmjs.com/package/otpauth)
```javascript
import * as OTPAuth from "otpauth";
// import * as OTPAuth from "otpauth/slim"; // Slim build without bundled dependencies.
// import * as OTPAuth from "otpauth/bare"; // Bare build with no bundled crypto (requires providing a custom HMAC function).
// Create a new TOTP object.
let totp = new OTPAuth.TOTP({
// Provider or service the account is associated with.
issuer: "ACME",
// Account identifier.
label: "Alice",
// Algorithm used for the HMAC function, possible values are:
// "SHA1", "SHA224", "SHA256", "SHA384", "SHA512",
// "SHA3-224", "SHA3-256", "SHA3-384" and "SHA3-512".
algorithm: "SHA1",
// Length of the generated tokens.
digits: 6,
// Interval of time for which a token is valid, in seconds.
period: 30,
// Arbitrary key encoded in base32 or `OTPAuth.Secret` instance
// (if omitted, a cryptographically secure random secret is generated).
secret: "US3WHSG7X5KAPV27VANWKQHF3SH3HULL",
// or: `OTPAuth.Secret.fromBase32("US3WHSG7X5KAPV27VANWKQHF3SH3HULL")`
// or: `new OTPAuth.Secret()`
// Custom HMAC function (required for bare build, optional otherwise).
// hmac: (algorithm, key, message) => Uint8Array,
});
// Unless you know what you are doing, it is recommended to use the default
// values for the algorithm, digits, and period options, as these are the most
// common values used by most services.
// Generate a cryptographically secure random secret.
// It is NOT recommended to use less than 128 bits (16 bytes).
let secret = new OTPAuth.Secret({ size: 20 });
// Generate a token (returns the current token as a string).
let token = totp.generate();
// Validate a token (returns the token delta or null if it is not found in the
// search window, in which case it should be considered invalid).
//
// A search window is useful to account for clock drift between the client and
// server; however, it should be kept as small as possible to prevent brute
// force attacks. In most cases, a value of 1 is sufficient. Furthermore, it is
// essential to implement a throttling mechanism on the server.
//
// For further details on the security considerations, it is advised to refer
// to Section 7 of RFC 4226 and Section 5 of RFC 6238:
// https://datatracker.ietf.org/doc/html/rfc4226#section-7
// https://datatracker.ietf.org/doc/html/rfc6238#section-5
let delta = totp.validate({ token, window: 1 });
// Get the counter value (number of intervals since the Unix epoch).
// Useful for implementing techniques against token reuse during the validity
// period.
let counter = totp.counter();
// Get the remaining milliseconds until the current token changes.
let remaining = totp.remaining();
// Convert to Google Authenticator key URI format.
// Usually the URI is encoded in a QR code that can be scanned by the user.
// This functionality is outside the scope of the project, but there are many
// libraries that can be used for this purpose, such as npmjs.com/package/qr
let uri = totp.toString();
// or: `OTPAuth.URI.stringify(totp)`
// returns: `otpauth://totp/ACME:Alice?issuer=ACME&secret=US3WHSG7X5KAPV27VANWKQHF3SH3HULL&algorithm=SHA1&digits=6&period=30`
// Convert from Google Authenticator key URI format.
totp = OTPAuth.URI.parse(uri);
```
### [Deno](https://jsr.io/@hectorm/otpauth)
```javascript
import * as OTPAuth from "jsr:@hectorm/otpauth";
// Same as above.
```
### [Browsers (ESM)](https://www.jsdelivr.com/package/npm/otpauth)
```html
<script type="importmap">
{
"imports": { "otpauth": "https://cdn.jsdelivr.net/npm/otpauth@%VERSION%/dist/otpauth.esm.min.js" },
"integrity": { "https://cdn.jsdelivr.net/npm/otpauth@%VERSION%/dist/otpauth.esm.min.js": "%HASH%" }
}
</script>
<script type="module">
import * as OTPAuth from "otpauth";
// Same as above.
</script>
```
### [Browsers (UMD)](https://www.jsdelivr.com/package/npm/otpauth)
```html
<script
src="https://cdn.jsdelivr.net/npm/otpauth@%VERSION%/dist/otpauth.umd.min.js"
integrity="%HASH%"
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script>
// Same as above.
</script>
```
## Documentation
For additional information, please refer to the documentation page at [hectorm.github.io/otpauth/](https://hectorm.github.io/otpauth/).
## License
[MIT License](https://github.com/hectorm/otpauth/blob/master/LICENSE.md)
© [Héctor Molinero Fernández](https://hector.molinero.dev/).

View File

@@ -0,0 +1,884 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
/**
* Converts an integer to an Uint8Array.
* @param {number} num Integer.
* @returns {Uint8Array} Uint8Array.
*/ const uintDecode = (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 arr;
};
/**
* "globalThis" ponyfill.
* @see [A horrifying globalThis polyfill in universal JavaScript](https://mathiasbynens.be/notes/globalthis)
* @type {Object.<string, *>}
*/ const globalScope = (()=>{
if (typeof globalThis === "object") return globalThis;
else {
Object.defineProperty(Object.prototype, "__GLOBALTHIS__", {
get () {
return this;
},
configurable: true
});
try {
// @ts-expect-error
// eslint-disable-next-line no-undef
if (typeof __GLOBALTHIS__ !== "undefined") return __GLOBALTHIS__;
} finally{
// @ts-expect-error
delete Object.prototype.__GLOBALTHIS__;
}
}
// Still unable to determine "globalThis", fall back to a naive method.
if (typeof self !== "undefined") return self;
else if (typeof window !== "undefined") return window;
else if (typeof global !== "undefined") return global;
return undefined;
})();
/**
* Canonicalizes a hash algorithm name.
* @param {string} algorithm Hash algorithm name.
* @returns {"SHA1"|"SHA224"|"SHA256"|"SHA384"|"SHA512"|"SHA3-224"|"SHA3-256"|"SHA3-384"|"SHA3-512"} Canonicalized hash algorithm name.
*/ const canonicalizeAlgorithm = (algorithm)=>{
switch(true){
case /^(?:SHA-?1|SSL3-SHA1)$/i.test(algorithm):
return "SHA1";
case /^SHA(?:2?-)?224$/i.test(algorithm):
return "SHA224";
case /^SHA(?:2?-)?256$/i.test(algorithm):
return "SHA256";
case /^SHA(?:2?-)?384$/i.test(algorithm):
return "SHA384";
case /^SHA(?:2?-)?512$/i.test(algorithm):
return "SHA512";
case /^SHA3-224$/i.test(algorithm):
return "SHA3-224";
case /^SHA3-256$/i.test(algorithm):
return "SHA3-256";
case /^SHA3-384$/i.test(algorithm):
return "SHA3-384";
case /^SHA3-512$/i.test(algorithm):
return "SHA3-512";
default:
throw new TypeError(`Unknown hash algorithm: ${algorithm}`);
}
};
/**
* Calculates an HMAC digest.
* @param {string} algorithm Algorithm.
* @param {Uint8Array} key Key.
* @param {Uint8Array} message Message.
* @returns {Uint8Array} Digest.
*/ const hmacDigest = (algorithm, key, message)=>{
{
throw new Error("Missing HMAC function");
}
};
/**
* RFC 4648 base32 alphabet without pad.
* @type {string}
*/ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
/**
* Converts a base32 string to an Uint8Array (RFC 4648).
* @see [LinusU/base32-decode](https://github.com/LinusU/base32-decode)
* @param {string} str Base32 string.
* @returns {Uint8Array} Uint8Array.
*/ const base32Decode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
// Canonicalize to all upper case and remove padding if it exists.
let end = str.length;
while(str[end - 1] === "=")--end;
str = (end < str.length ? str.substring(0, end) : str).toUpperCase();
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 = ALPHABET.indexOf(str[i]);
if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`);
value = value << 5 | idx;
bits += 5;
if (bits >= 8) {
bits -= 8;
arr[index++] = value >>> bits;
}
}
return arr;
};
/**
* Converts an Uint8Array to a base32 string (RFC 4648).
* @see [LinusU/base32-encode](https://github.com/LinusU/base32-encode)
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Base32 string.
*/ const base32Encode = (arr)=>{
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 += ALPHABET[value >>> bits - 5 & 31];
bits -= 5;
}
}
if (bits > 0) {
str += ALPHABET[value << 5 - bits & 31];
}
return str;
};
/**
* Converts a hexadecimal string to an Uint8Array.
* @param {string} str Hexadecimal string.
* @returns {Uint8Array} Uint8Array.
*/ const hexDecode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
const buf = new ArrayBuffer(str.length / 2);
const arr = new Uint8Array(buf);
for(let i = 0; i < str.length; i += 2){
arr[i / 2] = parseInt(str.substring(i, i + 2), 16);
}
return arr;
};
/**
* Converts an Uint8Array to a hexadecimal string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Hexadecimal string.
*/ const hexEncode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
const hex = arr[i].toString(16);
if (hex.length === 1) str += "0";
str += hex;
}
return str.toUpperCase();
};
/**
* Converts a Latin-1 string to an Uint8Array.
* @param {string} str Latin-1 string.
* @returns {Uint8Array} Uint8Array.
*/ const latin1Decode = (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) & 0xff;
}
return arr;
};
/**
* Converts an Uint8Array to a Latin-1 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Latin-1 string.
*/ const latin1Encode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
str += String.fromCharCode(arr[i]);
}
return str;
};
/**
* TextEncoder instance.
* @type {TextEncoder|null}
*/ const ENCODER = globalScope.TextEncoder ? new globalScope.TextEncoder() : null;
/**
* TextDecoder instance.
* @type {TextDecoder|null}
*/ const DECODER = globalScope.TextDecoder ? new globalScope.TextDecoder() : null;
/**
* Converts an UTF-8 string to an Uint8Array.
* @param {string} str String.
* @returns {Uint8Array} Uint8Array.
*/ const utf8Decode = (str)=>{
if (!ENCODER) {
throw new Error("Encoding API not available");
}
return ENCODER.encode(str);
};
/**
* Converts an Uint8Array to an UTF-8 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} String.
*/ const utf8Encode = (arr)=>{
if (!DECODER) {
throw new Error("Encoding API not available");
}
return DECODER.decode(arr);
};
/**
* Returns random bytes.
* @param {number} size Size.
* @returns {Uint8Array} Random bytes.
*/ const randomBytes = (size)=>{
if (globalScope.crypto?.getRandomValues) {
return globalScope.crypto.getRandomValues(new Uint8Array(size));
} else {
throw new Error("Cryptography API not available");
}
};
/**
* OTP secret key.
*/ class Secret {
/**
* Converts a Latin-1 string to a Secret object.
* @param {string} str Latin-1 string.
* @returns {Secret} Secret object.
*/ static fromLatin1(str) {
return new Secret({
buffer: latin1Decode(str).buffer
});
}
/**
* Converts an UTF-8 string to a Secret object.
* @param {string} str UTF-8 string.
* @returns {Secret} Secret object.
*/ static fromUTF8(str) {
return new Secret({
buffer: utf8Decode(str).buffer
});
}
/**
* Converts a base32 string to a Secret object.
* @param {string} str Base32 string.
* @returns {Secret} Secret object.
*/ static fromBase32(str) {
return new Secret({
buffer: base32Decode(str).buffer
});
}
/**
* Converts a hexadecimal string to a Secret object.
* @param {string} str Hexadecimal string.
* @returns {Secret} Secret object.
*/ static fromHex(str) {
return new Secret({
buffer: hexDecode(str).buffer
});
}
/**
* Secret key buffer.
* @deprecated For backward compatibility, the "bytes" property should be used instead.
* @type {ArrayBufferLike}
*/ get buffer() {
return this.bytes.buffer;
}
/**
* Latin-1 string representation of secret key.
* @type {string}
*/ get latin1() {
Object.defineProperty(this, "latin1", {
enumerable: true,
writable: false,
configurable: false,
value: latin1Encode(this.bytes)
});
return this.latin1;
}
/**
* UTF-8 string representation of secret key.
* @type {string}
*/ get utf8() {
Object.defineProperty(this, "utf8", {
enumerable: true,
writable: false,
configurable: false,
value: utf8Encode(this.bytes)
});
return this.utf8;
}
/**
* Base32 string representation of secret key.
* @type {string}
*/ get base32() {
Object.defineProperty(this, "base32", {
enumerable: true,
writable: false,
configurable: false,
value: base32Encode(this.bytes)
});
return this.base32;
}
/**
* Hexadecimal string representation of secret key.
* @type {string}
*/ get hex() {
Object.defineProperty(this, "hex", {
enumerable: true,
writable: false,
configurable: false,
value: hexEncode(this.bytes)
});
return this.hex;
}
/**
* Creates a secret key object.
* @param {Object} [config] Configuration options.
* @param {ArrayBufferLike} [config.buffer] Secret key buffer.
* @param {number} [config.size=20] Number of random bytes to generate, ignored if 'buffer' is provided.
*/ constructor({ buffer, size = 20 } = {}){
/**
* Secret key.
* @type {Uint8Array}
* @readonly
*/ this.bytes = typeof buffer === "undefined" ? randomBytes(size) : new Uint8Array(buffer);
// Prevent the "bytes" property from being modified.
Object.defineProperty(this, "bytes", {
enumerable: true,
writable: false,
configurable: false,
value: this.bytes
});
}
}
/**
* Returns true if a is equal to b, without leaking timing information that would allow an attacker to guess one of the values.
* @param {string} a String a.
* @param {string} b String b.
* @returns {boolean} Equality result.
*/ const timingSafeEqual = (a, b)=>{
{
if (a.length !== b.length) {
throw new TypeError("Input strings must have the same length");
}
let i = -1;
let out = 0;
while(++i < a.length){
out |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return out === 0;
}
};
/**
* HOTP: An HMAC-based One-time Password Algorithm.
* @see [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
*/ class HOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* counter: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
counter: 0,
window: 1
};
}
/**
* Generates an HOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac = hmacDigest }) {
const message = uintDecode(counter);
const digest = hmac(algorithm, secret.bytes, message);
if (!digest?.byteLength || digest.byteLength < 19) {
throw new TypeError("Return value must be at least 19 bytes");
}
const offset = digest[digest.byteLength - 1] & 15;
const otp = ((digest[offset] & 127) << 24 | (digest[offset + 1] & 255) << 16 | (digest[offset + 2] & 255) << 8 | digest[offset + 3] & 255) % 10 ** digits;
return otp.toString().padStart(digits, "0");
}
/**
* Generates an HOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.counter=this.counter++] Counter value.
* @returns {string} Token.
*/ generate({ counter = this.counter++ } = {}) {
return HOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
hmac: this.hmac
});
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, window = HOTP.defaults.window, hmac = hmacDigest }) {
// Return early if the token length does not match the digit number.
if (token.length !== digits) return null;
let delta = null;
const check = (/** @type {number} */ i)=>{
const generatedToken = HOTP.generate({
secret,
algorithm,
digits,
counter: i,
hmac
});
if (timingSafeEqual(token, generatedToken)) {
delta = i - counter;
}
};
check(counter);
for(let i = 1; i <= window && delta === null; ++i){
check(counter - i);
if (delta !== null) break;
check(counter + i);
if (delta !== null) break;
}
return delta;
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.counter=this.counter] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, counter = this.counter, window }) {
return HOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://hotp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `counter=${e(this.counter)}`;
}
/**
* Creates an HOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Initial counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = HOTP.defaults.issuer, label = HOTP.defaults.label, issuerInLabel = HOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Initial counter value.
* @type {number}
*/ this.counter = counter;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
*/ class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
period: 30,
window: 1
};
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ static counter({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return Math.floor(timestamp / 1000 / period);
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ counter({ timestamp = Date.now() } = {}) {
return TOTP.counter({
period: this.period,
timestamp
});
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ static remaining({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return period * 1000 - timestamp % (period * 1000);
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ remaining({ timestamp = Date.now() } = {}) {
return TOTP.remaining({
period: this.period,
timestamp
});
}
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), hmac }) {
return HOTP.generate({
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
hmac
});
}
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/ generate({ timestamp = Date.now() } = {}) {
return TOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
hmac: this.hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), window, hmac }) {
return HOTP.validate({
token,
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
window,
hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, timestamp, window }) {
return TOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://totp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `period=${e(this.period)}`;
}
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = TOTP.defaults.issuer, label = TOTP.defaults.label, issuerInLabel = TOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = TOTP.defaults.algorithm, digits = TOTP.defaults.digits, period = TOTP.defaults.period, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Token time-step duration.
* @type {number}
*/ this.period = period;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* Key URI regex (otpauth://TYPE/[ISSUER:]LABEL?PARAMETERS).
* @type {RegExp}
*/ const OTPURI_REGEX = /^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i;
/**
* RFC 4648 base32 alphabet with pad.
* @type {RegExp}
*/ const SECRET_REGEX = /^[2-7A-Z]+=*$/i;
/**
* Regex for supported algorithms in built-in HMAC function.
* @type {RegExp}
*/ const ALGORITHM_REGEX = /^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i;
/**
* Regex for custom algorithms in user-defined HMAC function.
* @type {RegExp}
*/ const ALGORITHM_CUSTOM_REGEX = /^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i;
/**
* Integer regex.
* @type {RegExp}
*/ const INTEGER_REGEX = /^[+-]?\d+$/;
/**
* Positive integer regex.
* @type {RegExp}
*/ const POSITIVE_INTEGER_REGEX = /^\+?[1-9]\d*$/;
/**
* HOTP/TOTP object/string conversion.
* @see [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
*/ class URI {
/**
* Parses a Google Authenticator key URI and returns an HOTP/TOTP object.
* @param {string} uri Google Authenticator Key URI.
* @param {Object} [config] Configuration options.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/ static parse(uri, { hmac } = {}) {
let uriGroups;
try {
uriGroups = uri.match(OTPURI_REGEX);
// eslint-disable-next-line no-unused-vars
} catch (_) {
/* Handled below */ }
if (!Array.isArray(uriGroups)) {
throw new URIError("Invalid URI format");
}
// Extract URI groups.
const uriType = uriGroups[1].toLowerCase();
const uriLabel = uriGroups[2].split(/(?::|%3A) *(.+)/i, 2).map(decodeURIComponent);
/** @type {Object.<string, string>} */ const uriParams = uriGroups[3].split("&").reduce((acc, cur)=>{
const pairArr = cur.split(/=(.*)/, 2).map(decodeURIComponent);
const pairKey = pairArr[0].toLowerCase();
const pairVal = pairArr[1];
/** @type {Object.<string, string>} */ const pairAcc = acc;
pairAcc[pairKey] = pairVal;
return pairAcc;
}, {});
// 'OTP' will be instantiated with 'config' argument.
let OTP;
const config = {};
if (uriType === "hotp") {
OTP = HOTP;
// Counter: required
if (typeof uriParams.counter !== "undefined" && INTEGER_REGEX.test(uriParams.counter)) {
config.counter = parseInt(uriParams.counter, 10);
} else {
throw new TypeError("Missing or invalid 'counter' parameter");
}
} else if (uriType === "totp") {
OTP = TOTP;
// Period: optional
if (typeof uriParams.period !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.period)) {
config.period = parseInt(uriParams.period, 10);
} else {
throw new TypeError("Invalid 'period' parameter");
}
}
} else {
throw new TypeError("Unknown OTP type");
}
// Label: required
// Issuer: optional
if (typeof uriParams.issuer !== "undefined") {
config.issuer = uriParams.issuer;
}
if (uriLabel.length === 2) {
config.label = uriLabel[1];
if (typeof config.issuer === "undefined" || config.issuer === "") {
config.issuer = uriLabel[0];
} else if (uriLabel[0] === "") {
config.issuerInLabel = false;
}
} else {
config.label = uriLabel[0];
if (typeof config.issuer !== "undefined" && config.issuer !== "") {
config.issuerInLabel = false;
}
}
// Secret: required
if (typeof uriParams.secret !== "undefined" && SECRET_REGEX.test(uriParams.secret)) {
config.secret = uriParams.secret;
} else {
throw new TypeError("Missing or invalid 'secret' parameter");
}
// Algorithm: optional
if (typeof uriParams.algorithm !== "undefined") {
if ((hmac ? ALGORITHM_CUSTOM_REGEX : ALGORITHM_REGEX).test(uriParams.algorithm)) {
config.algorithm = uriParams.algorithm;
} else {
throw new TypeError("Invalid 'algorithm' parameter");
}
}
// Digits: optional
if (typeof uriParams.digits !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.digits)) {
config.digits = parseInt(uriParams.digits, 10);
} else {
throw new TypeError("Invalid 'digits' parameter");
}
}
// HMAC: optional
if (typeof hmac !== "undefined") {
config.hmac = hmac;
}
return new OTP(config);
}
/**
* Converts an HOTP/TOTP object to a Google Authenticator key URI.
* @param {HOTP|TOTP} otp HOTP/TOTP object.
* @returns {string} Google Authenticator Key URI.
*/ static stringify(otp) {
if (otp instanceof HOTP || otp instanceof TOTP) {
return otp.toString();
}
throw new TypeError("Invalid 'HOTP/TOTP' object");
}
}
/**
* Library version.
* @type {string}
*/ const version = "9.5.0";
export { HOTP, Secret, TOTP, URI, version };

View File

@@ -0,0 +1,9 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
const e=(()=>{if("object"==typeof globalThis)return globalThis;Object.defineProperty(Object.prototype,"__GLOBALTHIS__",{get(){return this},configurable:!0});try{if("undefined"!=typeof __GLOBALTHIS__)return __GLOBALTHIS__}finally{delete Object.prototype.__GLOBALTHIS__}return"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0})(),t=e=>{switch(!0){case/^(?:SHA-?1|SSL3-SHA1)$/i.test(e):return"SHA1";case/^SHA(?:2?-)?224$/i.test(e):return"SHA224";case/^SHA(?:2?-)?256$/i.test(e):return"SHA256";case/^SHA(?:2?-)?384$/i.test(e):return"SHA384";case/^SHA(?:2?-)?512$/i.test(e):return"SHA512";case/^SHA3-224$/i.test(e):return"SHA3-224";case/^SHA3-256$/i.test(e):return"SHA3-256";case/^SHA3-384$/i.test(e):return"SHA3-384";case/^SHA3-512$/i.test(e):return"SHA3-512";default:throw new TypeError(`Unknown hash algorithm: ${e}`)}},r=(e,t,r)=>{throw new Error("Missing HMAC function")},i="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",s=e=>{let t=(e=e.replace(/ /g,"")).length;for(;"="===e[t-1];)--t;e=(t<e.length?e.substring(0,t):e).toUpperCase();const r=new ArrayBuffer(5*e.length/8|0),s=new Uint8Array(r);let n=0,a=0,o=0;for(let t=0;t<e.length;t++){const r=i.indexOf(e[t]);if(-1===r)throw new TypeError(`Invalid character found: ${e[t]}`);a=a<<5|r,n+=5,n>=8&&(n-=8,s[o++]=a>>>n)}return s},n=e=>{let t=0,r=0,s="";for(let n=0;n<e.length;n++)for(r=r<<8|e[n],t+=8;t>=5;)s+=i[r>>>t-5&31],t-=5;return t>0&&(s+=i[r<<5-t&31]),s},a=e=>{e=e.replace(/ /g,"");const t=new ArrayBuffer(e.length/2),r=new Uint8Array(t);for(let t=0;t<e.length;t+=2)r[t/2]=parseInt(e.substring(t,t+2),16);return r},o=e=>{let t="";for(let r=0;r<e.length;r++){const i=e[r].toString(16);1===i.length&&(t+="0"),t+=i}return t.toUpperCase()},l=e=>{const t=new ArrayBuffer(e.length),r=new Uint8Array(t);for(let t=0;t<e.length;t++)r[t]=255&e.charCodeAt(t);return r},u=e=>{let t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t},h=e.TextEncoder?new e.TextEncoder:null,c=e.TextDecoder?new e.TextDecoder:null,d=e=>{
if(!h)throw new Error("Encoding API not available");return h.encode(e)},g=e=>{if(!c)throw new Error("Encoding API not available");return c.decode(e)};class f{static fromLatin1(e){return new f({buffer:l(e).buffer})}static fromUTF8(e){return new f({buffer:d(e).buffer})}static fromBase32(e){return new f({buffer:s(e).buffer})}static fromHex(e){return new f({buffer:a(e).buffer})}get buffer(){return this.bytes.buffer}get latin1(){return Object.defineProperty(this,"latin1",{enumerable:!0,writable:!1,configurable:!1,value:u(this.bytes)}),this.latin1}get utf8(){return Object.defineProperty(this,"utf8",{enumerable:!0,writable:!1,configurable:!1,value:g(this.bytes)}),this.utf8}get base32(){return Object.defineProperty(this,"base32",{enumerable:!0,writable:!1,configurable:!1,value:n(this.bytes)}),this.base32}get hex(){return Object.defineProperty(this,"hex",{enumerable:!0,writable:!1,configurable:!1,value:o(this.bytes)}),this.hex}constructor({buffer:t,size:r=20}={}){this.bytes=void 0===t?(t=>{if(e.crypto?.getRandomValues)return e.crypto.getRandomValues(new Uint8Array(t));throw new Error("Cryptography API not available")})(r):new Uint8Array(t),Object.defineProperty(this,"bytes",{enumerable:!0,writable:!1,configurable:!1,value:this.bytes})}}class m{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,counter:0,window:1}}static generate({secret:e,algorithm:t=m.defaults.algorithm,digits:i=m.defaults.digits,counter:s=m.defaults.counter,hmac:n=r}){const a=(e=>{const t=new ArrayBuffer(8),r=new Uint8Array(t);let i=e;for(let e=7;e>=0&&0!==i;e--)r[e]=255&i,i-=r[e],i/=256;return r})(s),o=n(t,e.bytes,a);if(!o?.byteLength||o.byteLength<19)throw new TypeError("Return value must be at least 19 bytes");const l=15&o[o.byteLength-1];return(((127&o[l])<<24|(255&o[l+1])<<16|(255&o[l+2])<<8|255&o[l+3])%10**i).toString().padStart(i,"0")}generate({counter:e=this.counter++}={}){return m.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:e,hmac:this.hmac})}
static validate({token:e,secret:t,algorithm:i,digits:s=m.defaults.digits,counter:n=m.defaults.counter,window:a=m.defaults.window,hmac:o=r}){if(e.length!==s)return null;let l=null;const u=r=>{const a=m.generate({secret:t,algorithm:i,digits:s,counter:r,hmac:o});((e,t)=>{{if(e.length!==t.length)throw new TypeError("Input strings must have the same length");let r=-1,i=0;for(;++r<e.length;)i|=e.charCodeAt(r)^t.charCodeAt(r);return 0===i}})(e,a)&&(l=r-n)};u(n);for(let e=1;e<=a&&null===l&&(u(n-e),null===l)&&(u(n+e),null===l);++e);return l}validate({token:e,counter:t=this.counter,window:r}){return m.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://hotp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`counter=${e(this.counter)}`}constructor({issuer:e=m.defaults.issuer,label:r=m.defaults.label,issuerInLabel:i=m.defaults.issuerInLabel,secret:s=new f,algorithm:n=m.defaults.algorithm,digits:a=m.defaults.digits,counter:o=m.defaults.counter,hmac:l}={}){this.issuer=e,this.label=r,this.issuerInLabel=i,this.secret="string"==typeof s?f.fromBase32(s):s,this.algorithm=l?n:t(n),this.digits=a,this.counter=o,this.hmac=l}}class p{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,period:30,window:1}}static counter({period:e=p.defaults.period,timestamp:t=Date.now()}={}){return Math.floor(t/1e3/e)}counter({timestamp:e=Date.now()}={}){return p.counter({period:this.period,timestamp:e})}static remaining({period:e=p.defaults.period,timestamp:t=Date.now()}={}){return 1e3*e-t%(1e3*e)}remaining({timestamp:e=Date.now()}={}){return p.remaining({period:this.period,timestamp:e})}
static generate({secret:e,algorithm:t,digits:r,period:i=p.defaults.period,timestamp:s=Date.now(),hmac:n}){return m.generate({secret:e,algorithm:t,digits:r,counter:p.counter({period:i,timestamp:s}),hmac:n})}generate({timestamp:e=Date.now()}={}){return p.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:e,hmac:this.hmac})}static validate({token:e,secret:t,algorithm:r,digits:i,period:s=p.defaults.period,timestamp:n=Date.now(),window:a,hmac:o}){return m.validate({token:e,secret:t,algorithm:r,digits:i,counter:p.counter({period:s,timestamp:n}),window:a,hmac:o})}validate({token:e,timestamp:t,window:r}){return p.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://totp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`period=${e(this.period)}`}constructor({issuer:e=p.defaults.issuer,label:r=p.defaults.label,issuerInLabel:i=p.defaults.issuerInLabel,secret:s=new f,algorithm:n=p.defaults.algorithm,digits:a=p.defaults.digits,period:o=p.defaults.period,hmac:l}={}){this.issuer=e,this.label=r,this.issuerInLabel=i,this.secret="string"==typeof s?f.fromBase32(s):s,this.algorithm=l?n:t(n),this.digits=a,this.period=o,this.hmac=l}}const b=/^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i,w=/^[2-7A-Z]+=*$/i,y=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,A=/^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i,I=/^[+-]?\d+$/,$=/^\+?[1-9]\d*$/;class v{static parse(e,{hmac:t}={}){let r;try{r=e.match(b)}catch(e){}if(!Array.isArray(r))throw new URIError("Invalid URI format");const i=r[1].toLowerCase(),s=r[2].split(/(?::|%3A) *(.+)/i,2).map(decodeURIComponent),n=r[3].split("&").reduce((e,t)=>{
const r=t.split(/=(.*)/,2).map(decodeURIComponent),i=r[0].toLowerCase(),s=r[1],n=e;return n[i]=s,n},{});let a;const o={};if("hotp"===i){if(a=m,void 0===n.counter||!I.test(n.counter))throw new TypeError("Missing or invalid 'counter' parameter");o.counter=parseInt(n.counter,10)}else{if("totp"!==i)throw new TypeError("Unknown OTP type");if(a=p,void 0!==n.period){if(!$.test(n.period))throw new TypeError("Invalid 'period' parameter");o.period=parseInt(n.period,10)}}if(void 0!==n.issuer&&(o.issuer=n.issuer),2===s.length?(o.label=s[1],void 0===o.issuer||""===o.issuer?o.issuer=s[0]:""===s[0]&&(o.issuerInLabel=!1)):(o.label=s[0],void 0!==o.issuer&&""!==o.issuer&&(o.issuerInLabel=!1)),void 0===n.secret||!w.test(n.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(o.secret=n.secret,void 0!==n.algorithm){if(!(t?A:y).test(n.algorithm))throw new TypeError("Invalid 'algorithm' parameter");o.algorithm=n.algorithm}if(void 0!==n.digits){if(!$.test(n.digits))throw new TypeError("Invalid 'digits' parameter");o.digits=parseInt(n.digits,10)}return void 0!==t&&(o.hmac=t),new a(o)}static stringify(e){if(e instanceof m||e instanceof p)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}const S="9.5.0";export{m as HOTP,f as Secret,p as TOTP,v as URI,S as version};
//# sourceMappingURL=otpauth.bare.esm.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,456 @@
/**
* OTP secret key.
*/
declare class Secret {
/**
* Converts a Latin-1 string to a Secret object.
* @param {string} str Latin-1 string.
* @returns {Secret} Secret object.
*/
static fromLatin1(str: string): Secret;
/**
* Converts an UTF-8 string to a Secret object.
* @param {string} str UTF-8 string.
* @returns {Secret} Secret object.
*/
static fromUTF8(str: string): Secret;
/**
* Converts a base32 string to a Secret object.
* @param {string} str Base32 string.
* @returns {Secret} Secret object.
*/
static fromBase32(str: string): Secret;
/**
* Converts a hexadecimal string to a Secret object.
* @param {string} str Hexadecimal string.
* @returns {Secret} Secret object.
*/
static fromHex(str: string): Secret;
/**
* Creates a secret key object.
* @param {Object} [config] Configuration options.
* @param {ArrayBufferLike} [config.buffer] Secret key buffer.
* @param {number} [config.size=20] Number of random bytes to generate, ignored if 'buffer' is provided.
*/
constructor({ buffer, size }?: {
buffer?: ArrayBufferLike | undefined;
size?: number | undefined;
});
/**
* Secret key.
* @type {Uint8Array}
* @readonly
*/
readonly bytes: Uint8Array;
/**
* Secret key buffer.
* @deprecated For backward compatibility, the "bytes" property should be used instead.
* @type {ArrayBufferLike}
*/
get buffer(): ArrayBufferLike;
/**
* Latin-1 string representation of secret key.
* @type {string}
*/
get latin1(): string;
/**
* UTF-8 string representation of secret key.
* @type {string}
*/
get utf8(): string;
/**
* Base32 string representation of secret key.
* @type {string}
*/
get base32(): string;
/**
* Hexadecimal string representation of secret key.
* @type {string}
*/
get hex(): string;
}
/**
* HOTP: An HMAC-based One-time Password Algorithm.
* @see [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
*/
declare class HOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* counter: number
* window: number
* }}
*/
static get defaults(): {
issuer: string;
label: string;
issuerInLabel: boolean;
algorithm: string;
digits: number;
counter: number;
window: number;
};
/**
* Generates an HOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/
static generate({ secret, algorithm, digits, counter, hmac, }: {
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
counter?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): string;
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
static validate({ token, secret, algorithm, digits, counter, window, hmac, }: {
token: string;
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
counter?: number | undefined;
window?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): number | null;
/**
* Creates an HOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Initial counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/
constructor({ issuer, label, issuerInLabel, secret, algorithm, digits, counter, hmac, }?: {
issuer?: string | undefined;
label?: string | undefined;
issuerInLabel?: boolean | undefined;
secret?: string | Secret | undefined;
algorithm?: string | undefined;
digits?: number | undefined;
counter?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
});
/**
* Account provider.
* @type {string}
*/
issuer: string;
/**
* Account label.
* @type {string}
*/
label: string;
/**
* Include issuer prefix in label.
* @type {boolean}
*/
issuerInLabel: boolean;
/**
* Secret key.
* @type {Secret}
*/
secret: Secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/
algorithm: string;
/**
* Token length.
* @type {number}
*/
digits: number;
/**
* Initial counter value.
* @type {number}
*/
counter: number;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/
hmac: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
/**
* Generates an HOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.counter=this.counter++] Counter value.
* @returns {string} Token.
*/
generate({ counter }?: {
counter?: number | undefined;
}): string;
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.counter=this.counter] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
validate({ token, counter, window }: {
token: string;
counter?: number | undefined;
window?: number | undefined;
}): number | null;
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/
toString(): string;
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
*/
declare class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/
static get defaults(): {
issuer: string;
label: string;
issuerInLabel: boolean;
algorithm: string;
digits: number;
period: number;
window: number;
};
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/
static counter({ period, timestamp }?: {
period?: number | undefined;
timestamp?: number | undefined;
}): number;
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/
static remaining({ period, timestamp }?: {
period?: number | undefined;
timestamp?: number | undefined;
}): number;
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/
static generate({ secret, algorithm, digits, period, timestamp, hmac }: {
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
period?: number | undefined;
timestamp?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): string;
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
static validate({ token, secret, algorithm, digits, period, timestamp, window, hmac, }: {
token: string;
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
period?: number | undefined;
timestamp?: number | undefined;
window?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): number | null;
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/
constructor({ issuer, label, issuerInLabel, secret, algorithm, digits, period, hmac, }?: {
issuer?: string | undefined;
label?: string | undefined;
issuerInLabel?: boolean | undefined;
secret?: string | Secret | undefined;
algorithm?: string | undefined;
digits?: number | undefined;
period?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
});
/**
* Account provider.
* @type {string}
*/
issuer: string;
/**
* Account label.
* @type {string}
*/
label: string;
/**
* Include issuer prefix in label.
* @type {boolean}
*/
issuerInLabel: boolean;
/**
* Secret key.
* @type {Secret}
*/
secret: Secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/
algorithm: string;
/**
* Token length.
* @type {number}
*/
digits: number;
/**
* Token time-step duration.
* @type {number}
*/
period: number;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/
hmac: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/
counter({ timestamp }?: {
timestamp?: number | undefined;
}): number;
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/
remaining({ timestamp }?: {
timestamp?: number | undefined;
}): number;
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/
generate({ timestamp }?: {
timestamp?: number | undefined;
}): string;
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
validate({ token, timestamp, window }: {
token: string;
timestamp?: number | undefined;
window?: number | undefined;
}): number | null;
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/
toString(): string;
}
/**
* HOTP/TOTP object/string conversion.
* @see [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
*/
declare class URI {
/**
* Parses a Google Authenticator key URI and returns an HOTP/TOTP object.
* @param {string} uri Google Authenticator Key URI.
* @param {Object} [config] Configuration options.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/
static parse(uri: string, { hmac }?: {
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): HOTP | TOTP;
/**
* Converts an HOTP/TOTP object to a Google Authenticator key URI.
* @param {HOTP|TOTP} otp HOTP/TOTP object.
* @returns {string} Google Authenticator Key URI.
*/
static stringify(otp: HOTP | TOTP): string;
}
/**
* Library version.
* @type {string}
*/
declare const version: string;
export { HOTP, Secret, TOTP, URI, version };

View File

@@ -0,0 +1,456 @@
/**
* OTP secret key.
*/
declare class Secret {
/**
* Converts a Latin-1 string to a Secret object.
* @param {string} str Latin-1 string.
* @returns {Secret} Secret object.
*/
static fromLatin1(str: string): Secret;
/**
* Converts an UTF-8 string to a Secret object.
* @param {string} str UTF-8 string.
* @returns {Secret} Secret object.
*/
static fromUTF8(str: string): Secret;
/**
* Converts a base32 string to a Secret object.
* @param {string} str Base32 string.
* @returns {Secret} Secret object.
*/
static fromBase32(str: string): Secret;
/**
* Converts a hexadecimal string to a Secret object.
* @param {string} str Hexadecimal string.
* @returns {Secret} Secret object.
*/
static fromHex(str: string): Secret;
/**
* Creates a secret key object.
* @param {Object} [config] Configuration options.
* @param {ArrayBufferLike} [config.buffer] Secret key buffer.
* @param {number} [config.size=20] Number of random bytes to generate, ignored if 'buffer' is provided.
*/
constructor({ buffer, size }?: {
buffer?: ArrayBufferLike | undefined;
size?: number | undefined;
});
/**
* Secret key.
* @type {Uint8Array}
* @readonly
*/
readonly bytes: Uint8Array;
/**
* Secret key buffer.
* @deprecated For backward compatibility, the "bytes" property should be used instead.
* @type {ArrayBufferLike}
*/
get buffer(): ArrayBufferLike;
/**
* Latin-1 string representation of secret key.
* @type {string}
*/
get latin1(): string;
/**
* UTF-8 string representation of secret key.
* @type {string}
*/
get utf8(): string;
/**
* Base32 string representation of secret key.
* @type {string}
*/
get base32(): string;
/**
* Hexadecimal string representation of secret key.
* @type {string}
*/
get hex(): string;
}
/**
* HOTP: An HMAC-based One-time Password Algorithm.
* @see [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
*/
declare class HOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* counter: number
* window: number
* }}
*/
static get defaults(): {
issuer: string;
label: string;
issuerInLabel: boolean;
algorithm: string;
digits: number;
counter: number;
window: number;
};
/**
* Generates an HOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/
static generate({ secret, algorithm, digits, counter, hmac, }: {
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
counter?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): string;
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
static validate({ token, secret, algorithm, digits, counter, window, hmac, }: {
token: string;
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
counter?: number | undefined;
window?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): number | null;
/**
* Creates an HOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Initial counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/
constructor({ issuer, label, issuerInLabel, secret, algorithm, digits, counter, hmac, }?: {
issuer?: string | undefined;
label?: string | undefined;
issuerInLabel?: boolean | undefined;
secret?: string | Secret | undefined;
algorithm?: string | undefined;
digits?: number | undefined;
counter?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
});
/**
* Account provider.
* @type {string}
*/
issuer: string;
/**
* Account label.
* @type {string}
*/
label: string;
/**
* Include issuer prefix in label.
* @type {boolean}
*/
issuerInLabel: boolean;
/**
* Secret key.
* @type {Secret}
*/
secret: Secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/
algorithm: string;
/**
* Token length.
* @type {number}
*/
digits: number;
/**
* Initial counter value.
* @type {number}
*/
counter: number;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/
hmac: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
/**
* Generates an HOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.counter=this.counter++] Counter value.
* @returns {string} Token.
*/
generate({ counter }?: {
counter?: number | undefined;
}): string;
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.counter=this.counter] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
validate({ token, counter, window }: {
token: string;
counter?: number | undefined;
window?: number | undefined;
}): number | null;
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/
toString(): string;
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
*/
declare class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/
static get defaults(): {
issuer: string;
label: string;
issuerInLabel: boolean;
algorithm: string;
digits: number;
period: number;
window: number;
};
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/
static counter({ period, timestamp }?: {
period?: number | undefined;
timestamp?: number | undefined;
}): number;
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/
static remaining({ period, timestamp }?: {
period?: number | undefined;
timestamp?: number | undefined;
}): number;
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/
static generate({ secret, algorithm, digits, period, timestamp, hmac }: {
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
period?: number | undefined;
timestamp?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): string;
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
static validate({ token, secret, algorithm, digits, period, timestamp, window, hmac, }: {
token: string;
secret: Secret;
algorithm?: string | undefined;
digits?: number | undefined;
period?: number | undefined;
timestamp?: number | undefined;
window?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): number | null;
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/
constructor({ issuer, label, issuerInLabel, secret, algorithm, digits, period, hmac, }?: {
issuer?: string | undefined;
label?: string | undefined;
issuerInLabel?: boolean | undefined;
secret?: string | Secret | undefined;
algorithm?: string | undefined;
digits?: number | undefined;
period?: number | undefined;
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
});
/**
* Account provider.
* @type {string}
*/
issuer: string;
/**
* Account label.
* @type {string}
*/
label: string;
/**
* Include issuer prefix in label.
* @type {boolean}
*/
issuerInLabel: boolean;
/**
* Secret key.
* @type {Secret}
*/
secret: Secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/
algorithm: string;
/**
* Token length.
* @type {number}
*/
digits: number;
/**
* Token time-step duration.
* @type {number}
*/
period: number;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/
hmac: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/
counter({ timestamp }?: {
timestamp?: number | undefined;
}): number;
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/
remaining({ timestamp }?: {
timestamp?: number | undefined;
}): number;
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/
generate({ timestamp }?: {
timestamp?: number | undefined;
}): string;
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/
validate({ token, timestamp, window }: {
token: string;
timestamp?: number | undefined;
window?: number | undefined;
}): number | null;
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/
toString(): string;
}
/**
* HOTP/TOTP object/string conversion.
* @see [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
*/
declare class URI {
/**
* Parses a Google Authenticator key URI and returns an HOTP/TOTP object.
* @param {string} uri Google Authenticator Key URI.
* @param {Object} [config] Configuration options.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/
static parse(uri: string, { hmac }?: {
hmac?: ((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array) | undefined;
}): HOTP | TOTP;
/**
* Converts an HOTP/TOTP object to a Google Authenticator key URI.
* @param {HOTP|TOTP} otp HOTP/TOTP object.
* @returns {string} Google Authenticator Key URI.
*/
static stringify(otp: HOTP | TOTP): string;
}
/**
* Library version.
* @type {string}
*/
declare const version: string;
export { HOTP, Secret, TOTP, URI, version };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
//! noble-hashes 2.0.1 | (c) Paul Miller | MIT | https://github.com/paulmillr/noble-hashes
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
function t(t,e=""){if(!Number.isSafeInteger(t)||t<0)throw new Error(`${e&&`"${e}" `}expected integer >= 0, got ${t}`)}function e(t,e,s=""){const i=(r=t)instanceof Uint8Array||ArrayBuffer.isView(r)&&"Uint8Array"===r.constructor.name;var r;const n=t?.length,o=void 0!==e;if(!i||o&&n!==e)throw new Error((s&&`"${s}" `)+"expected Uint8Array"+(o?` of length ${e}`:"")+", got "+(i?`length=${n}`:"type="+typeof t));return t}function s(t,e=!0){if(t.destroyed)throw new Error("Hash instance has been destroyed");if(e&&t.finished)throw new Error("Hash#digest() has already been called")}function i(t,s){e(t,void 0,"digestInto() output");const i=s.outputLen;if(t.length<i)throw new Error('"digestInto() output" expected to be of length >='+i)}function r(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function n(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function o(t,e){return t<<32-e|t>>>e}function h(t,e){return t<<e|t>>>32-e>>>0}function a(t){return t<<24&4278190080|t<<8&16711680|t>>>8&65280|t>>>24&255}const c=(()=>68===new Uint8Array(new Uint32Array([287454020]).buffer)[0])()?t=>t:function(t){for(let e=0;e<t.length;e++)t[e]=a(t[e]);return t};function l(t,e={}){const s=(e,s)=>t(s).update(e).digest(),i=t(void 0);return s.outputLen=i.outputLen,s.blockLen=i.blockLen,s.create=e=>t(e),Object.assign(s,e),Object.freeze(s)}const u=t=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,t])});class f{update(t){return s(this),this.iHash.update(t),this}digestInto(t){s(this),e(t,this.outputLen,"output"),this.finished=!0,this.iHash.digestInto(t),this.oHash.update(t),this.oHash.digestInto(t),this.destroy()}digest(){const t=new Uint8Array(this.oHash.outputLen);return this.digestInto(t),t}_cloneInto(t){t||(t=Object.create(Object.getPrototypeOf(this),{}));const{oHash:e,iHash:s,finished:i,destroyed:r,blockLen:n,outputLen:o}=this;return t.finished=i,t.destroyed=r,t.blockLen=n,t.outputLen=o,t.oHash=e._cloneInto(t.oHash),t.iHash=s._cloneInto(t.iHash),t}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),
this.iHash.destroy()}constructor(s,i){if(this.finished=!1,this.destroyed=!1,function(e){if("function"!=typeof e||"function"!=typeof e.create)throw new Error("Hash must wrapped by utils.createHasher");t(e.outputLen),t(e.blockLen)}(s),e(i,void 0,"key"),this.iHash=s.create(),"function"!=typeof this.iHash.update)throw new Error("Expected instance of class which extends utils.Hash");this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;const n=this.blockLen,o=new Uint8Array(n);o.set(i.length>n?s.create().update(i).digest():i);for(let t=0;t<o.length;t++)o[t]^=54;this.iHash.update(o),this.oHash=s.create();for(let t=0;t<o.length;t++)o[t]^=106;this.oHash.update(o),r(o)}}const d=(t,e,s)=>new f(t,e).update(s).digest();function b(t,e,s){return t&e^~t&s}function g(t,e,s){return t&e^t&s^e&s}d.create=(t,e)=>new f(t,e);class p{update(t){s(this),e(t);const{view:i,buffer:r,blockLen:o}=this,h=t.length;for(let e=0;e<h;){const s=Math.min(o-this.pos,h-e);if(s===o){const s=n(t);for(;o<=h-e;e+=o)this.process(s,e);continue}r.set(t.subarray(e,e+s),this.pos),this.pos+=s,e+=s,this.pos===o&&(this.process(i,0),this.pos=0)}return this.length+=t.length,this.roundClean(),this}digestInto(t){s(this),i(t,this),this.finished=!0;const{buffer:e,view:o,blockLen:h,isLE:a}=this;let{pos:c}=this;e[c++]=128,r(this.buffer.subarray(c)),this.padOffset>h-c&&(this.process(o,0),c=0);for(let t=c;t<h;t++)e[t]=0;o.setBigUint64(h-8,BigInt(8*this.length),a),this.process(o,0);const l=n(t),u=this.outputLen;if(u%4)throw new Error("_sha2: outputLen must be aligned to 32bit");const f=u/4,d=this.get();if(f>d.length)throw new Error("_sha2: outputLen bigger than state");for(let t=0;t<f;t++)l.setUint32(4*t,d[t],a)}digest(){const{buffer:t,outputLen:e}=this;this.digestInto(t);const s=t.slice(0,e);return this.destroy(),s}_cloneInto(t){t||(t=new this.constructor),t.set(...this.get());const{blockLen:e,buffer:s,length:i,finished:r,destroyed:n,pos:o}=this;return t.destroyed=n,t.finished=r,t.length=i,t.pos=o,i%e&&t.buffer.set(s),t}clone(){return this._cloneInto()}
constructor(t,e,s,i){this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=t,this.outputLen=e,this.padOffset=s,this.isLE=i,this.buffer=new Uint8Array(t),this.view=n(this.buffer)}}const w=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),m=Uint32Array.from([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428]),y=Uint32Array.from([3418070365,3238371032,1654270250,914150663,2438529370,812702999,355462360,4144912697,1731405415,4290775857,2394180231,1750603025,3675008525,1694076839,1203062813,3204075428]),A=Uint32Array.from([1779033703,4089235720,3144134277,2227873595,1013904242,4271175723,2773480762,1595750129,1359893119,2917565137,2600822924,725511199,528734635,4215389547,1541459225,327033209]),x=Uint32Array.from([1732584193,4023233417,2562383102,271733878,3285377520]),H=new Uint32Array(80);class I extends p{get(){const{A:t,B:e,C:s,D:i,E:r}=this;return[t,e,s,i,r]}set(t,e,s,i,r){this.A=0|t,this.B=0|e,this.C=0|s,this.D=0|i,this.E=0|r}process(t,e){for(let s=0;s<16;s++,e+=4)H[s]=t.getUint32(e,!1);for(let t=16;t<80;t++)H[t]=h(H[t-3]^H[t-8]^H[t-14]^H[t-16],1);let{A:s,B:i,C:r,D:n,E:o}=this;for(let t=0;t<80;t++){let e,a;t<20?(e=b(i,r,n),a=1518500249):t<40?(e=i^r^n,a=1859775393):t<60?(e=g(i,r,n),a=2400959708):(e=i^r^n,a=3395469782);const c=h(s,5)+e+o+a+H[t]|0;o=n,n=r,r=h(i,30),i=s,s=c}s=s+this.A|0,i=i+this.B|0,r=r+this.C|0,n=n+this.D|0,o=o+this.E|0,this.set(s,i,r,n,o)}roundClean(){r(H)}destroy(){this.set(0,0,0,0,0),r(this.buffer)}constructor(){super(64,20,8,!1),this.A=0|x[0],this.B=0|x[1],this.C=0|x[2],this.D=0|x[3],this.E=0|x[4]}}const L=l(()=>new I),E=BigInt(2**32-1),U=BigInt(32);function B(t,e=!1){return e?{h:Number(t&E),l:Number(t>>U&E)}:{h:0|Number(t>>U&E),l:0|Number(t&E)}}function S(t,e=!1){const s=t.length;let i=new Uint32Array(s),r=new Uint32Array(s);for(let n=0;n<s;n++){const{h:s,l:o}=B(t[n],e);[i[n],r[n]]=[s,o]}return[i,r]}
const v=(t,e,s)=>t>>>s,C=(t,e,s)=>t<<32-s|e>>>s,O=(t,e,s)=>t>>>s|e<<32-s,$=(t,e,s)=>t<<32-s|e>>>s,k=(t,e,s)=>t<<64-s|e>>>s-32,D=(t,e,s)=>t>>>s-32|e<<64-s;function T(t,e,s,i){const r=(e>>>0)+(i>>>0);return{h:t+s+(r/2**32|0)|0,l:0|r}}const _=(t,e,s)=>(t>>>0)+(e>>>0)+(s>>>0),F=(t,e,s,i)=>e+s+i+(t/2**32|0)|0,G=(t,e,s,i)=>(t>>>0)+(e>>>0)+(s>>>0)+(i>>>0),P=(t,e,s,i,r)=>e+s+i+r+(t/2**32|0)|0,j=(t,e,s,i,r)=>(t>>>0)+(e>>>0)+(s>>>0)+(i>>>0)+(r>>>0),M=(t,e,s,i,r,n)=>e+s+i+r+n+(t/2**32|0)|0,R=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),X=new Uint32Array(64);class N extends p{get(){const{A:t,B:e,C:s,D:i,E:r,F:n,G:o,H:h}=this;return[t,e,s,i,r,n,o,h]}set(t,e,s,i,r,n,o,h){this.A=0|t,this.B=0|e,this.C=0|s,this.D=0|i,this.E=0|r,this.F=0|n,this.G=0|o,this.H=0|h}process(t,e){for(let s=0;s<16;s++,e+=4)X[s]=t.getUint32(e,!1);for(let t=16;t<64;t++){const e=X[t-15],s=X[t-2],i=o(e,7)^o(e,18)^e>>>3,r=o(s,17)^o(s,19)^s>>>10;X[t]=r+X[t-7]+i+X[t-16]|0}let{A:s,B:i,C:r,D:n,E:h,F:a,G:c,H:l}=this;for(let t=0;t<64;t++){const e=l+(o(h,6)^o(h,11)^o(h,25))+b(h,a,c)+R[t]+X[t]|0,u=(o(s,2)^o(s,13)^o(s,22))+g(s,i,r)|0;l=c,c=a,a=h,h=n+e|0,n=r,r=i,i=s,s=e+u|0}s=s+this.A|0,i=i+this.B|0,r=r+this.C|0,n=n+this.D|0,h=h+this.E|0,a=a+this.F|0,c=c+this.G|0,l=l+this.H|0,this.set(s,i,r,n,h,a,c,l)}roundClean(){r(X)}destroy(){this.set(0,0,0,0,0,0,0,0),r(this.buffer)}constructor(t){super(64,t,8,!1)}}
class Z extends N{constructor(){super(32),this.A=0|w[0],this.B=0|w[1],this.C=0|w[2],this.D=0|w[3],this.E=0|w[4],this.F=0|w[5],this.G=0|w[6],this.H=0|w[7]}}class V extends N{constructor(){super(28),this.A=0|m[0],this.B=0|m[1],this.C=0|m[2],this.D=0|m[3],this.E=0|m[4],this.F=0|m[5],this.G=0|m[6],this.H=0|m[7]}}
const z=(()=>S(["0x428a2f98d728ae22","0x7137449123ef65cd","0xb5c0fbcfec4d3b2f","0xe9b5dba58189dbbc","0x3956c25bf348b538","0x59f111f1b605d019","0x923f82a4af194f9b","0xab1c5ed5da6d8118","0xd807aa98a3030242","0x12835b0145706fbe","0x243185be4ee4b28c","0x550c7dc3d5ffb4e2","0x72be5d74f27b896f","0x80deb1fe3b1696b1","0x9bdc06a725c71235","0xc19bf174cf692694","0xe49b69c19ef14ad2","0xefbe4786384f25e3","0x0fc19dc68b8cd5b5","0x240ca1cc77ac9c65","0x2de92c6f592b0275","0x4a7484aa6ea6e483","0x5cb0a9dcbd41fbd4","0x76f988da831153b5","0x983e5152ee66dfab","0xa831c66d2db43210","0xb00327c898fb213f","0xbf597fc7beef0ee4","0xc6e00bf33da88fc2","0xd5a79147930aa725","0x06ca6351e003826f","0x142929670a0e6e70","0x27b70a8546d22ffc","0x2e1b21385c26c926","0x4d2c6dfc5ac42aed","0x53380d139d95b3df","0x650a73548baf63de","0x766a0abb3c77b2a8","0x81c2c92e47edaee6","0x92722c851482353b","0xa2bfe8a14cf10364","0xa81a664bbc423001","0xc24b8b70d0f89791","0xc76c51a30654be30","0xd192e819d6ef5218","0xd69906245565a910","0xf40e35855771202a","0x106aa07032bbd1b8","0x19a4c116b8d2d0c8","0x1e376c085141ab53","0x2748774cdf8eeb99","0x34b0bcb5e19b48a8","0x391c0cb3c5c95a63","0x4ed8aa4ae3418acb","0x5b9cca4f7763e373","0x682e6ff3d6b2b8a3","0x748f82ee5defb2fc","0x78a5636f43172f60","0x84c87814a1f0ab72","0x8cc702081a6439ec","0x90befffa23631e28","0xa4506cebde82bde9","0xbef9a3f7b2c67915","0xc67178f2e372532b","0xca273eceea26619c","0xd186b8c721c0c207","0xeada7dd6cde0eb1e","0xf57d4f7fee6ed178","0x06f067aa72176fba","0x0a637dc5a2c898a6","0x113f9804bef90dae","0x1b710b35131c471b","0x28db77f523047d84","0x32caab7b40c72493","0x3c9ebe0a15c9bebc","0x431d67c49c100d4c","0x4cc5d4becb3e42b6","0x597f299cfc657e2a","0x5fcb6fab3ad6faec","0x6c44198c4a475817"].map(t=>BigInt(t))))(),J=(()=>z[0])(),K=(()=>z[1])(),Q=new Uint32Array(80),W=new Uint32Array(80);class Y extends p{get(){const{Ah:t,Al:e,Bh:s,Bl:i,Ch:r,Cl:n,Dh:o,Dl:h,Eh:a,El:c,Fh:l,Fl:u,Gh:f,Gl:d,Hh:b,Hl:g}=this;return[t,e,s,i,r,n,o,h,a,c,l,u,f,d,b,g]}set(t,e,s,i,r,n,o,h,a,c,l,u,f,d,b,g){this.Ah=0|t,this.Al=0|e,this.Bh=0|s,this.Bl=0|i,this.Ch=0|r,
this.Cl=0|n,this.Dh=0|o,this.Dl=0|h,this.Eh=0|a,this.El=0|c,this.Fh=0|l,this.Fl=0|u,this.Gh=0|f,this.Gl=0|d,this.Hh=0|b,this.Hl=0|g}process(t,e){for(let s=0;s<16;s++,e+=4)Q[s]=t.getUint32(e),W[s]=t.getUint32(e+=4);for(let t=16;t<80;t++){const e=0|Q[t-15],s=0|W[t-15],i=O(e,s,1)^O(e,s,8)^v(e,0,7),r=$(e,s,1)^$(e,s,8)^C(e,s,7),n=0|Q[t-2],o=0|W[t-2],h=O(n,o,19)^k(n,o,61)^v(n,0,6),a=$(n,o,19)^D(n,o,61)^C(n,o,6),c=G(r,a,W[t-7],W[t-16]),l=P(c,i,h,Q[t-7],Q[t-16]);Q[t]=0|l,W[t]=0|c}let{Ah:s,Al:i,Bh:r,Bl:n,Ch:o,Cl:h,Dh:a,Dl:c,Eh:l,El:u,Fh:f,Fl:d,Gh:b,Gl:g,Hh:p,Hl:w}=this;for(let t=0;t<80;t++){const e=O(l,u,14)^O(l,u,18)^k(l,u,41),m=$(l,u,14)^$(l,u,18)^D(l,u,41),y=l&f^~l&b,A=j(w,m,u&d^~u&g,K[t],W[t]),x=M(A,p,e,y,J[t],Q[t]),H=0|A,I=O(s,i,28)^k(s,i,34)^k(s,i,39),L=$(s,i,28)^D(s,i,34)^D(s,i,39),E=s&r^s&o^r&o,U=i&n^i&h^n&h;p=0|b,w=0|g,b=0|f,g=0|d,f=0|l,d=0|u,({h:l,l:u}=T(0|a,0|c,0|x,0|H)),a=0|o,c=0|h,o=0|r,h=0|n,r=0|s,n=0|i;const B=_(H,L,U);s=F(B,x,I,E),i=0|B}({h:s,l:i}=T(0|this.Ah,0|this.Al,0|s,0|i)),({h:r,l:n}=T(0|this.Bh,0|this.Bl,0|r,0|n)),({h:o,l:h}=T(0|this.Ch,0|this.Cl,0|o,0|h)),({h:a,l:c}=T(0|this.Dh,0|this.Dl,0|a,0|c)),({h:l,l:u}=T(0|this.Eh,0|this.El,0|l,0|u)),({h:f,l:d}=T(0|this.Fh,0|this.Fl,0|f,0|d)),({h:b,l:g}=T(0|this.Gh,0|this.Gl,0|b,0|g)),({h:p,l:w}=T(0|this.Hh,0|this.Hl,0|p,0|w)),this.set(s,i,r,n,o,h,a,c,l,u,f,d,b,g,p,w)}roundClean(){r(Q,W)}destroy(){r(this.buffer),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}constructor(t){super(128,t,16,!1)}}class q extends Y{constructor(){super(64),this.Ah=0|A[0],this.Al=0|A[1],this.Bh=0|A[2],this.Bl=0|A[3],this.Ch=0|A[4],this.Cl=0|A[5],this.Dh=0|A[6],this.Dl=0|A[7],this.Eh=0|A[8],this.El=0|A[9],this.Fh=0|A[10],this.Fl=0|A[11],this.Gh=0|A[12],this.Gl=0|A[13],this.Hh=0|A[14],this.Hl=0|A[15]}}class tt extends Y{constructor(){super(48),this.Ah=0|y[0],this.Al=0|y[1],this.Bh=0|y[2],this.Bl=0|y[3],this.Ch=0|y[4],this.Cl=0|y[5],this.Dh=0|y[6],this.Dl=0|y[7],this.Eh=0|y[8],this.El=0|y[9],this.Fh=0|y[10],this.Fl=0|y[11],this.Gh=0|y[12],this.Gl=0|y[13],this.Hh=0|y[14],this.Hl=0|y[15]}}
const et=l(()=>new Z,u(1)),st=l(()=>new V,u(4)),it=l(()=>new q,u(3)),rt=l(()=>new tt,u(2)),nt=BigInt(0),ot=BigInt(1),ht=BigInt(2),at=BigInt(7),ct=BigInt(256),lt=BigInt(113),ut=[],ft=[],dt=[];for(let t=0,e=ot,s=1,i=0;t<24;t++){[s,i]=[i,(2*s+3*i)%5],ut.push(2*(5*i+s)),ft.push((t+1)*(t+2)/2%64);let r=nt;for(let t=0;t<7;t++)e=(e<<ot^(e>>at)*lt)%ct,e&ht&&(r^=ot<<(ot<<BigInt(t))-ot);dt.push(r)}const bt=S(dt,!0),gt=bt[0],pt=bt[1],wt=(t,e,s)=>s>32?((t,e,s)=>e<<s-32|t>>>64-s)(t,e,s):((t,e,s)=>t<<s|e>>>32-s)(t,e,s),mt=(t,e,s)=>s>32?((t,e,s)=>t<<s-32|e>>>64-s)(t,e,s):((t,e,s)=>e<<s|t>>>32-s)(t,e,s);class yt{clone(){return this._cloneInto()}keccak(){c(this.state32),function(t,e=24){const s=new Uint32Array(10);for(let i=24-e;i<24;i++){for(let e=0;e<10;e++)s[e]=t[e]^t[e+10]^t[e+20]^t[e+30]^t[e+40];for(let e=0;e<10;e+=2){const i=(e+8)%10,r=(e+2)%10,n=s[r],o=s[r+1],h=wt(n,o,1)^s[i],a=mt(n,o,1)^s[i+1];for(let s=0;s<50;s+=10)t[e+s]^=h,t[e+s+1]^=a}let e=t[2],r=t[3];for(let s=0;s<24;s++){const i=ft[s],n=wt(e,r,i),o=mt(e,r,i),h=ut[s];e=t[h],r=t[h+1],t[h]=n,t[h+1]=o}for(let e=0;e<50;e+=10){for(let i=0;i<10;i++)s[i]=t[e+i];for(let i=0;i<10;i++)t[e+i]^=~s[(i+2)%10]&s[(i+4)%10]}t[0]^=gt[i],t[1]^=pt[i]}r(s)}(this.state32,this.rounds),c(this.state32),this.posOut=0,this.pos=0}update(t){s(this),e(t);const{blockLen:i,state:r}=this,n=t.length;for(let e=0;e<n;){const s=Math.min(i-this.pos,n-e);for(let i=0;i<s;i++)r[this.pos++]^=t[e++];this.pos===i&&this.keccak()}return this}finish(){if(this.finished)return;this.finished=!0;const{state:t,suffix:e,pos:s,blockLen:i}=this;t[s]^=e,128&e&&s===i-1&&this.keccak(),t[i-1]^=128,this.keccak()}writeInto(t){s(this,!1),e(t),this.finish();const i=this.state,{blockLen:r}=this;for(let e=0,s=t.length;e<s;){this.posOut>=r&&this.keccak();const n=Math.min(r-this.posOut,s-e);t.set(i.subarray(this.posOut,this.posOut+n),e),this.posOut+=n,e+=n}return t}xofInto(t){if(!this.enableXOF)throw new Error("XOF is not possible for this instance");return this.writeInto(t)}xof(e){return t(e),this.xofInto(new Uint8Array(e))}
digestInto(t){if(i(t,this),this.finished)throw new Error("digest() was already called");return this.writeInto(t),this.destroy(),t}digest(){return this.digestInto(new Uint8Array(this.outputLen))}destroy(){this.destroyed=!0,r(this.state)}_cloneInto(t){const{blockLen:e,suffix:s,outputLen:i,rounds:r,enableXOF:n}=this;return t||(t=new yt(e,s,i,n,r)),t.state32.set(this.state32),t.pos=this.pos,t.posOut=this.posOut,t.finished=this.finished,t.rounds=r,t.suffix=s,t.outputLen=i,t.enableXOF=n,t.destroyed=this.destroyed,t}constructor(e,s,i,r=!1,n=24){if(this.pos=0,this.posOut=0,this.finished=!1,this.destroyed=!1,this.enableXOF=!1,this.blockLen=e,this.suffix=s,this.outputLen=i,this.enableXOF=r,this.rounds=n,t(i,"outputLen"),!(0<e&&e<200))throw new Error("only keccak-f1600 function is supported");var o;this.state=new Uint8Array(200),this.state32=(o=this.state,new Uint32Array(o.buffer,o.byteOffset,Math.floor(o.byteLength/4)))}}const At=(t,e,s,i={})=>l(()=>new yt(e,t,s),i),xt=At(6,144,28,u(7)),Ht=At(6,136,32,u(8)),It=At(6,104,48,u(9)),Lt=At(6,72,64,u(10)),Et=(()=>{if("object"==typeof globalThis)return globalThis;Object.defineProperty(Object.prototype,"__GLOBALTHIS__",{get(){return this},configurable:!0});try{if("undefined"!=typeof __GLOBALTHIS__)return __GLOBALTHIS__}finally{delete Object.prototype.__GLOBALTHIS__}return"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0})(),Ut={SHA1:L,SHA224:st,SHA256:et,SHA384:rt,SHA512:it,"SHA3-224":xt,"SHA3-256":Ht,"SHA3-384":It,"SHA3-512":Lt},Bt=t=>{switch(!0){case/^(?:SHA-?1|SSL3-SHA1)$/i.test(t):return"SHA1";case/^SHA(?:2?-)?224$/i.test(t):return"SHA224";case/^SHA(?:2?-)?256$/i.test(t):return"SHA256";case/^SHA(?:2?-)?384$/i.test(t):return"SHA384";case/^SHA(?:2?-)?512$/i.test(t):return"SHA512";case/^SHA3-224$/i.test(t):return"SHA3-224";case/^SHA3-256$/i.test(t):return"SHA3-256";case/^SHA3-384$/i.test(t):return"SHA3-384";case/^SHA3-512$/i.test(t):return"SHA3-512";default:throw new TypeError(`Unknown hash algorithm: ${t}`)}},St=(t,e,s)=>{
if(d){const i=Ut[t]??Ut[Bt(t)];return d(i,e,s)}throw new Error("Missing HMAC function")},vt="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",Ct=t=>{let e=(t=t.replace(/ /g,"")).length;for(;"="===t[e-1];)--e;t=(e<t.length?t.substring(0,e):t).toUpperCase();const s=new ArrayBuffer(5*t.length/8|0),i=new Uint8Array(s);let r=0,n=0,o=0;for(let e=0;e<t.length;e++){const s=vt.indexOf(t[e]);if(-1===s)throw new TypeError(`Invalid character found: ${t[e]}`);n=n<<5|s,r+=5,r>=8&&(r-=8,i[o++]=n>>>r)}return i},Ot=t=>{let e=0,s=0,i="";for(let r=0;r<t.length;r++)for(s=s<<8|t[r],e+=8;e>=5;)i+=vt[s>>>e-5&31],e-=5;return e>0&&(i+=vt[s<<5-e&31]),i},$t=t=>{t=t.replace(/ /g,"");const e=new ArrayBuffer(t.length/2),s=new Uint8Array(e);for(let e=0;e<t.length;e+=2)s[e/2]=parseInt(t.substring(e,e+2),16);return s},kt=t=>{let e="";for(let s=0;s<t.length;s++){const i=t[s].toString(16);1===i.length&&(e+="0"),e+=i}return e.toUpperCase()},Dt=t=>{const e=new ArrayBuffer(t.length),s=new Uint8Array(e);for(let e=0;e<t.length;e++)s[e]=255&t.charCodeAt(e);return s},Tt=t=>{let e="";for(let s=0;s<t.length;s++)e+=String.fromCharCode(t[s]);return e},_t=Et.TextEncoder?new Et.TextEncoder:null,Ft=Et.TextDecoder?new Et.TextDecoder:null,Gt=t=>{if(!_t)throw new Error("Encoding API not available");return _t.encode(t)},Pt=t=>{if(!Ft)throw new Error("Encoding API not available");return Ft.decode(t)};class jt{static fromLatin1(t){return new jt({buffer:Dt(t).buffer})}static fromUTF8(t){return new jt({buffer:Gt(t).buffer})}static fromBase32(t){return new jt({buffer:Ct(t).buffer})}static fromHex(t){return new jt({buffer:$t(t).buffer})}get buffer(){return this.bytes.buffer}get latin1(){return Object.defineProperty(this,"latin1",{enumerable:!0,writable:!1,configurable:!1,value:Tt(this.bytes)}),this.latin1}get utf8(){return Object.defineProperty(this,"utf8",{enumerable:!0,writable:!1,configurable:!1,value:Pt(this.bytes)}),this.utf8}get base32(){return Object.defineProperty(this,"base32",{enumerable:!0,writable:!1,configurable:!1,value:Ot(this.bytes)}),this.base32}get hex(){
return Object.defineProperty(this,"hex",{enumerable:!0,writable:!1,configurable:!1,value:kt(this.bytes)}),this.hex}constructor({buffer:t,size:e=20}={}){this.bytes=void 0===t?(t=>{if(Et.crypto?.getRandomValues)return Et.crypto.getRandomValues(new Uint8Array(t));throw new Error("Cryptography API not available")})(e):new Uint8Array(t),Object.defineProperty(this,"bytes",{enumerable:!0,writable:!1,configurable:!1,value:this.bytes})}}class Mt{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,counter:0,window:1}}static generate({secret:t,algorithm:e=Mt.defaults.algorithm,digits:s=Mt.defaults.digits,counter:i=Mt.defaults.counter,hmac:r=St}){const n=(t=>{const e=new ArrayBuffer(8),s=new Uint8Array(e);let i=t;for(let t=7;t>=0&&0!==i;t--)s[t]=255&i,i-=s[t],i/=256;return s})(i),o=r(e,t.bytes,n);if(!o?.byteLength||o.byteLength<19)throw new TypeError("Return value must be at least 19 bytes");const h=15&o[o.byteLength-1];return(((127&o[h])<<24|(255&o[h+1])<<16|(255&o[h+2])<<8|255&o[h+3])%10**s).toString().padStart(s,"0")}generate({counter:t=this.counter++}={}){return Mt.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:t,hmac:this.hmac})}static validate({token:t,secret:e,algorithm:s,digits:i=Mt.defaults.digits,counter:r=Mt.defaults.counter,window:n=Mt.defaults.window,hmac:o=St}){if(t.length!==i)return null;let h=null;const a=n=>{const a=Mt.generate({secret:e,algorithm:s,digits:i,counter:n,hmac:o});((t,e)=>{{if(t.length!==e.length)throw new TypeError("Input strings must have the same length");let s=-1,i=0;for(;++s<t.length;)i|=t.charCodeAt(s)^e.charCodeAt(s);return 0===i}})(t,a)&&(h=n-r)};a(r);for(let t=1;t<=n&&null===h&&(a(r-t),null===h)&&(a(r+t),null===h);++t);return h}validate({token:t,counter:e=this.counter,window:s}){return Mt.validate({token:t,secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:e,window:s,hmac:this.hmac})}toString(){const t=encodeURIComponent
;return"otpauth://hotp/"+(this.issuer.length>0?this.issuerInLabel?`${t(this.issuer)}:${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?`)+`secret=${t(this.secret.base32)}&`+`algorithm=${t(this.algorithm)}&`+`digits=${t(this.digits)}&`+`counter=${t(this.counter)}`}constructor({issuer:t=Mt.defaults.issuer,label:e=Mt.defaults.label,issuerInLabel:s=Mt.defaults.issuerInLabel,secret:i=new jt,algorithm:r=Mt.defaults.algorithm,digits:n=Mt.defaults.digits,counter:o=Mt.defaults.counter,hmac:h}={}){this.issuer=t,this.label=e,this.issuerInLabel=s,this.secret="string"==typeof i?jt.fromBase32(i):i,this.algorithm=h?r:Bt(r),this.digits=n,this.counter=o,this.hmac=h}}class Rt{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,period:30,window:1}}static counter({period:t=Rt.defaults.period,timestamp:e=Date.now()}={}){return Math.floor(e/1e3/t)}counter({timestamp:t=Date.now()}={}){return Rt.counter({period:this.period,timestamp:t})}static remaining({period:t=Rt.defaults.period,timestamp:e=Date.now()}={}){return 1e3*t-e%(1e3*t)}remaining({timestamp:t=Date.now()}={}){return Rt.remaining({period:this.period,timestamp:t})}static generate({secret:t,algorithm:e,digits:s,period:i=Rt.defaults.period,timestamp:r=Date.now(),hmac:n}){return Mt.generate({secret:t,algorithm:e,digits:s,counter:Rt.counter({period:i,timestamp:r}),hmac:n})}generate({timestamp:t=Date.now()}={}){return Rt.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:t,hmac:this.hmac})}static validate({token:t,secret:e,algorithm:s,digits:i,period:r=Rt.defaults.period,timestamp:n=Date.now(),window:o,hmac:h}){return Mt.validate({token:t,secret:e,algorithm:s,digits:i,counter:Rt.counter({period:r,timestamp:n}),window:o,hmac:h})}validate({token:t,timestamp:e,window:s}){return Rt.validate({token:t,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:e,window:s,hmac:this.hmac})}toString(){
const t=encodeURIComponent;return"otpauth://totp/"+(this.issuer.length>0?this.issuerInLabel?`${t(this.issuer)}:${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?`)+`secret=${t(this.secret.base32)}&`+`algorithm=${t(this.algorithm)}&`+`digits=${t(this.digits)}&`+`period=${t(this.period)}`}constructor({issuer:t=Rt.defaults.issuer,label:e=Rt.defaults.label,issuerInLabel:s=Rt.defaults.issuerInLabel,secret:i=new jt,algorithm:r=Rt.defaults.algorithm,digits:n=Rt.defaults.digits,period:o=Rt.defaults.period,hmac:h}={}){this.issuer=t,this.label=e,this.issuerInLabel=s,this.secret="string"==typeof i?jt.fromBase32(i):i,this.algorithm=h?r:Bt(r),this.digits=n,this.period=o,this.hmac=h}}const Xt=/^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i,Nt=/^[2-7A-Z]+=*$/i,Zt=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,Vt=/^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i,zt=/^[+-]?\d+$/,Jt=/^\+?[1-9]\d*$/;class Kt{static parse(t,{hmac:e}={}){let s;try{s=t.match(Xt)}catch(t){}if(!Array.isArray(s))throw new URIError("Invalid URI format");const i=s[1].toLowerCase(),r=s[2].split(/(?::|%3A) *(.+)/i,2).map(decodeURIComponent),n=s[3].split("&").reduce((t,e)=>{const s=e.split(/=(.*)/,2).map(decodeURIComponent),i=s[0].toLowerCase(),r=s[1],n=t;return n[i]=r,n},{});let o;const h={};if("hotp"===i){if(o=Mt,void 0===n.counter||!zt.test(n.counter))throw new TypeError("Missing or invalid 'counter' parameter");h.counter=parseInt(n.counter,10)}else{if("totp"!==i)throw new TypeError("Unknown OTP type");if(o=Rt,void 0!==n.period){if(!Jt.test(n.period))throw new TypeError("Invalid 'period' parameter");h.period=parseInt(n.period,10)}}if(void 0!==n.issuer&&(h.issuer=n.issuer),2===r.length?(h.label=r[1],void 0===h.issuer||""===h.issuer?h.issuer=r[0]:""===r[0]&&(h.issuerInLabel=!1)):(h.label=r[0],void 0!==h.issuer&&""!==h.issuer&&(h.issuerInLabel=!1)),void 0===n.secret||!Nt.test(n.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(h.secret=n.secret,
void 0!==n.algorithm){if(!(e?Vt:Zt).test(n.algorithm))throw new TypeError("Invalid 'algorithm' parameter");h.algorithm=n.algorithm}if(void 0!==n.digits){if(!Jt.test(n.digits))throw new TypeError("Invalid 'digits' parameter");h.digits=parseInt(n.digits,10)}return void 0!==e&&(h.hmac=e),new o(h)}static stringify(t){if(t instanceof Mt||t instanceof Rt)return t.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}const Qt="9.5.0";export{Mt as HOTP,jt as Secret,Rt as TOTP,Kt as URI,Qt as version};
//# sourceMappingURL=otpauth.esm.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,919 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
'use strict';
var crypto = require('node:crypto');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
/**
* Converts an integer to an Uint8Array.
* @param {number} num Integer.
* @returns {Uint8Array} Uint8Array.
*/ const uintDecode = (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 arr;
};
/**
* "globalThis" ponyfill.
* @see [A horrifying globalThis polyfill in universal JavaScript](https://mathiasbynens.be/notes/globalthis)
* @type {Object.<string, *>}
*/ const globalScope = (()=>{
if (typeof globalThis === "object") return globalThis;
else {
Object.defineProperty(Object.prototype, "__GLOBALTHIS__", {
get () {
return this;
},
configurable: true
});
try {
// @ts-expect-error
// eslint-disable-next-line no-undef
if (typeof __GLOBALTHIS__ !== "undefined") return __GLOBALTHIS__;
} finally{
// @ts-expect-error
delete Object.prototype.__GLOBALTHIS__;
}
}
// Still unable to determine "globalThis", fall back to a naive method.
if (typeof self !== "undefined") return self;
else if (typeof window !== "undefined") return window;
else if (typeof global !== "undefined") return global;
return undefined;
})();
/**
* Canonicalizes a hash algorithm name.
* @param {string} algorithm Hash algorithm name.
* @returns {"SHA1"|"SHA224"|"SHA256"|"SHA384"|"SHA512"|"SHA3-224"|"SHA3-256"|"SHA3-384"|"SHA3-512"} Canonicalized hash algorithm name.
*/ const canonicalizeAlgorithm = (algorithm)=>{
switch(true){
case /^(?:SHA-?1|SSL3-SHA1)$/i.test(algorithm):
return "SHA1";
case /^SHA(?:2?-)?224$/i.test(algorithm):
return "SHA224";
case /^SHA(?:2?-)?256$/i.test(algorithm):
return "SHA256";
case /^SHA(?:2?-)?384$/i.test(algorithm):
return "SHA384";
case /^SHA(?:2?-)?512$/i.test(algorithm):
return "SHA512";
case /^SHA3-224$/i.test(algorithm):
return "SHA3-224";
case /^SHA3-256$/i.test(algorithm):
return "SHA3-256";
case /^SHA3-384$/i.test(algorithm):
return "SHA3-384";
case /^SHA3-512$/i.test(algorithm):
return "SHA3-512";
default:
throw new TypeError(`Unknown hash algorithm: ${algorithm}`);
}
};
/**
* Calculates an HMAC digest.
* @param {string} algorithm Algorithm.
* @param {Uint8Array} key Key.
* @param {Uint8Array} message Message.
* @returns {Uint8Array} Digest.
*/ const hmacDigest = (algorithm, key, message)=>{
if (crypto__namespace?.createHmac) {
const hmac = crypto__namespace.createHmac(algorithm, globalScope.Buffer.from(key));
hmac.update(globalScope.Buffer.from(message));
return hmac.digest();
} else {
throw new Error("Missing HMAC function");
}
};
/**
* RFC 4648 base32 alphabet without pad.
* @type {string}
*/ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
/**
* Converts a base32 string to an Uint8Array (RFC 4648).
* @see [LinusU/base32-decode](https://github.com/LinusU/base32-decode)
* @param {string} str Base32 string.
* @returns {Uint8Array} Uint8Array.
*/ const base32Decode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
// Canonicalize to all upper case and remove padding if it exists.
let end = str.length;
while(str[end - 1] === "=")--end;
str = (end < str.length ? str.substring(0, end) : str).toUpperCase();
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 = ALPHABET.indexOf(str[i]);
if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`);
value = value << 5 | idx;
bits += 5;
if (bits >= 8) {
bits -= 8;
arr[index++] = value >>> bits;
}
}
return arr;
};
/**
* Converts an Uint8Array to a base32 string (RFC 4648).
* @see [LinusU/base32-encode](https://github.com/LinusU/base32-encode)
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Base32 string.
*/ const base32Encode = (arr)=>{
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 += ALPHABET[value >>> bits - 5 & 31];
bits -= 5;
}
}
if (bits > 0) {
str += ALPHABET[value << 5 - bits & 31];
}
return str;
};
/**
* Converts a hexadecimal string to an Uint8Array.
* @param {string} str Hexadecimal string.
* @returns {Uint8Array} Uint8Array.
*/ const hexDecode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
const buf = new ArrayBuffer(str.length / 2);
const arr = new Uint8Array(buf);
for(let i = 0; i < str.length; i += 2){
arr[i / 2] = parseInt(str.substring(i, i + 2), 16);
}
return arr;
};
/**
* Converts an Uint8Array to a hexadecimal string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Hexadecimal string.
*/ const hexEncode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
const hex = arr[i].toString(16);
if (hex.length === 1) str += "0";
str += hex;
}
return str.toUpperCase();
};
/**
* Converts a Latin-1 string to an Uint8Array.
* @param {string} str Latin-1 string.
* @returns {Uint8Array} Uint8Array.
*/ const latin1Decode = (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) & 0xff;
}
return arr;
};
/**
* Converts an Uint8Array to a Latin-1 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Latin-1 string.
*/ const latin1Encode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
str += String.fromCharCode(arr[i]);
}
return str;
};
/**
* TextEncoder instance.
* @type {TextEncoder|null}
*/ const ENCODER = globalScope.TextEncoder ? new globalScope.TextEncoder() : null;
/**
* TextDecoder instance.
* @type {TextDecoder|null}
*/ const DECODER = globalScope.TextDecoder ? new globalScope.TextDecoder() : null;
/**
* Converts an UTF-8 string to an Uint8Array.
* @param {string} str String.
* @returns {Uint8Array} Uint8Array.
*/ const utf8Decode = (str)=>{
if (!ENCODER) {
throw new Error("Encoding API not available");
}
return ENCODER.encode(str);
};
/**
* Converts an Uint8Array to an UTF-8 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} String.
*/ const utf8Encode = (arr)=>{
if (!DECODER) {
throw new Error("Encoding API not available");
}
return DECODER.decode(arr);
};
/**
* Returns random bytes.
* @param {number} size Size.
* @returns {Uint8Array} Random bytes.
*/ const randomBytes = (size)=>{
if (crypto__namespace?.randomBytes) {
return crypto__namespace.randomBytes(size);
} else if (globalScope.crypto?.getRandomValues) {
return globalScope.crypto.getRandomValues(new Uint8Array(size));
} else {
throw new Error("Cryptography API not available");
}
};
/**
* OTP secret key.
*/ class Secret {
/**
* Converts a Latin-1 string to a Secret object.
* @param {string} str Latin-1 string.
* @returns {Secret} Secret object.
*/ static fromLatin1(str) {
return new Secret({
buffer: latin1Decode(str).buffer
});
}
/**
* Converts an UTF-8 string to a Secret object.
* @param {string} str UTF-8 string.
* @returns {Secret} Secret object.
*/ static fromUTF8(str) {
return new Secret({
buffer: utf8Decode(str).buffer
});
}
/**
* Converts a base32 string to a Secret object.
* @param {string} str Base32 string.
* @returns {Secret} Secret object.
*/ static fromBase32(str) {
return new Secret({
buffer: base32Decode(str).buffer
});
}
/**
* Converts a hexadecimal string to a Secret object.
* @param {string} str Hexadecimal string.
* @returns {Secret} Secret object.
*/ static fromHex(str) {
return new Secret({
buffer: hexDecode(str).buffer
});
}
/**
* Secret key buffer.
* @deprecated For backward compatibility, the "bytes" property should be used instead.
* @type {ArrayBufferLike}
*/ get buffer() {
return this.bytes.buffer;
}
/**
* Latin-1 string representation of secret key.
* @type {string}
*/ get latin1() {
Object.defineProperty(this, "latin1", {
enumerable: true,
writable: false,
configurable: false,
value: latin1Encode(this.bytes)
});
return this.latin1;
}
/**
* UTF-8 string representation of secret key.
* @type {string}
*/ get utf8() {
Object.defineProperty(this, "utf8", {
enumerable: true,
writable: false,
configurable: false,
value: utf8Encode(this.bytes)
});
return this.utf8;
}
/**
* Base32 string representation of secret key.
* @type {string}
*/ get base32() {
Object.defineProperty(this, "base32", {
enumerable: true,
writable: false,
configurable: false,
value: base32Encode(this.bytes)
});
return this.base32;
}
/**
* Hexadecimal string representation of secret key.
* @type {string}
*/ get hex() {
Object.defineProperty(this, "hex", {
enumerable: true,
writable: false,
configurable: false,
value: hexEncode(this.bytes)
});
return this.hex;
}
/**
* Creates a secret key object.
* @param {Object} [config] Configuration options.
* @param {ArrayBufferLike} [config.buffer] Secret key buffer.
* @param {number} [config.size=20] Number of random bytes to generate, ignored if 'buffer' is provided.
*/ constructor({ buffer, size = 20 } = {}){
/**
* Secret key.
* @type {Uint8Array}
* @readonly
*/ this.bytes = typeof buffer === "undefined" ? randomBytes(size) : new Uint8Array(buffer);
// Prevent the "bytes" property from being modified.
Object.defineProperty(this, "bytes", {
enumerable: true,
writable: false,
configurable: false,
value: this.bytes
});
}
}
/**
* Returns true if a is equal to b, without leaking timing information that would allow an attacker to guess one of the values.
* @param {string} a String a.
* @param {string} b String b.
* @returns {boolean} Equality result.
*/ const timingSafeEqual = (a, b)=>{
if (crypto__namespace?.timingSafeEqual) {
return crypto__namespace.timingSafeEqual(globalScope.Buffer.from(a), globalScope.Buffer.from(b));
} else {
if (a.length !== b.length) {
throw new TypeError("Input strings must have the same length");
}
let i = -1;
let out = 0;
while(++i < a.length){
out |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return out === 0;
}
};
/**
* HOTP: An HMAC-based One-time Password Algorithm.
* @see [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
*/ class HOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* counter: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
counter: 0,
window: 1
};
}
/**
* Generates an HOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac = hmacDigest }) {
const message = uintDecode(counter);
const digest = hmac(algorithm, secret.bytes, message);
if (!digest?.byteLength || digest.byteLength < 19) {
throw new TypeError("Return value must be at least 19 bytes");
}
const offset = digest[digest.byteLength - 1] & 15;
const otp = ((digest[offset] & 127) << 24 | (digest[offset + 1] & 255) << 16 | (digest[offset + 2] & 255) << 8 | digest[offset + 3] & 255) % 10 ** digits;
return otp.toString().padStart(digits, "0");
}
/**
* Generates an HOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.counter=this.counter++] Counter value.
* @returns {string} Token.
*/ generate({ counter = this.counter++ } = {}) {
return HOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
hmac: this.hmac
});
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, window = HOTP.defaults.window, hmac = hmacDigest }) {
// Return early if the token length does not match the digit number.
if (token.length !== digits) return null;
let delta = null;
const check = (/** @type {number} */ i)=>{
const generatedToken = HOTP.generate({
secret,
algorithm,
digits,
counter: i,
hmac
});
if (timingSafeEqual(token, generatedToken)) {
delta = i - counter;
}
};
check(counter);
for(let i = 1; i <= window && delta === null; ++i){
check(counter - i);
if (delta !== null) break;
check(counter + i);
if (delta !== null) break;
}
return delta;
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.counter=this.counter] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, counter = this.counter, window }) {
return HOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://hotp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `counter=${e(this.counter)}`;
}
/**
* Creates an HOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Initial counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = HOTP.defaults.issuer, label = HOTP.defaults.label, issuerInLabel = HOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Initial counter value.
* @type {number}
*/ this.counter = counter;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
*/ class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
period: 30,
window: 1
};
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ static counter({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return Math.floor(timestamp / 1000 / period);
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ counter({ timestamp = Date.now() } = {}) {
return TOTP.counter({
period: this.period,
timestamp
});
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ static remaining({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return period * 1000 - timestamp % (period * 1000);
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ remaining({ timestamp = Date.now() } = {}) {
return TOTP.remaining({
period: this.period,
timestamp
});
}
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), hmac }) {
return HOTP.generate({
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
hmac
});
}
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/ generate({ timestamp = Date.now() } = {}) {
return TOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
hmac: this.hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), window, hmac }) {
return HOTP.validate({
token,
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
window,
hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, timestamp, window }) {
return TOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://totp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `period=${e(this.period)}`;
}
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = TOTP.defaults.issuer, label = TOTP.defaults.label, issuerInLabel = TOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = TOTP.defaults.algorithm, digits = TOTP.defaults.digits, period = TOTP.defaults.period, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Token time-step duration.
* @type {number}
*/ this.period = period;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* Key URI regex (otpauth://TYPE/[ISSUER:]LABEL?PARAMETERS).
* @type {RegExp}
*/ const OTPURI_REGEX = /^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i;
/**
* RFC 4648 base32 alphabet with pad.
* @type {RegExp}
*/ const SECRET_REGEX = /^[2-7A-Z]+=*$/i;
/**
* Regex for supported algorithms in built-in HMAC function.
* @type {RegExp}
*/ const ALGORITHM_REGEX = /^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i;
/**
* Regex for custom algorithms in user-defined HMAC function.
* @type {RegExp}
*/ const ALGORITHM_CUSTOM_REGEX = /^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i;
/**
* Integer regex.
* @type {RegExp}
*/ const INTEGER_REGEX = /^[+-]?\d+$/;
/**
* Positive integer regex.
* @type {RegExp}
*/ const POSITIVE_INTEGER_REGEX = /^\+?[1-9]\d*$/;
/**
* HOTP/TOTP object/string conversion.
* @see [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
*/ class URI {
/**
* Parses a Google Authenticator key URI and returns an HOTP/TOTP object.
* @param {string} uri Google Authenticator Key URI.
* @param {Object} [config] Configuration options.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/ static parse(uri, { hmac } = {}) {
let uriGroups;
try {
uriGroups = uri.match(OTPURI_REGEX);
// eslint-disable-next-line no-unused-vars
} catch (_) {
/* Handled below */ }
if (!Array.isArray(uriGroups)) {
throw new URIError("Invalid URI format");
}
// Extract URI groups.
const uriType = uriGroups[1].toLowerCase();
const uriLabel = uriGroups[2].split(/(?::|%3A) *(.+)/i, 2).map(decodeURIComponent);
/** @type {Object.<string, string>} */ const uriParams = uriGroups[3].split("&").reduce((acc, cur)=>{
const pairArr = cur.split(/=(.*)/, 2).map(decodeURIComponent);
const pairKey = pairArr[0].toLowerCase();
const pairVal = pairArr[1];
/** @type {Object.<string, string>} */ const pairAcc = acc;
pairAcc[pairKey] = pairVal;
return pairAcc;
}, {});
// 'OTP' will be instantiated with 'config' argument.
let OTP;
const config = {};
if (uriType === "hotp") {
OTP = HOTP;
// Counter: required
if (typeof uriParams.counter !== "undefined" && INTEGER_REGEX.test(uriParams.counter)) {
config.counter = parseInt(uriParams.counter, 10);
} else {
throw new TypeError("Missing or invalid 'counter' parameter");
}
} else if (uriType === "totp") {
OTP = TOTP;
// Period: optional
if (typeof uriParams.period !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.period)) {
config.period = parseInt(uriParams.period, 10);
} else {
throw new TypeError("Invalid 'period' parameter");
}
}
} else {
throw new TypeError("Unknown OTP type");
}
// Label: required
// Issuer: optional
if (typeof uriParams.issuer !== "undefined") {
config.issuer = uriParams.issuer;
}
if (uriLabel.length === 2) {
config.label = uriLabel[1];
if (typeof config.issuer === "undefined" || config.issuer === "") {
config.issuer = uriLabel[0];
} else if (uriLabel[0] === "") {
config.issuerInLabel = false;
}
} else {
config.label = uriLabel[0];
if (typeof config.issuer !== "undefined" && config.issuer !== "") {
config.issuerInLabel = false;
}
}
// Secret: required
if (typeof uriParams.secret !== "undefined" && SECRET_REGEX.test(uriParams.secret)) {
config.secret = uriParams.secret;
} else {
throw new TypeError("Missing or invalid 'secret' parameter");
}
// Algorithm: optional
if (typeof uriParams.algorithm !== "undefined") {
if ((hmac ? ALGORITHM_CUSTOM_REGEX : ALGORITHM_REGEX).test(uriParams.algorithm)) {
config.algorithm = uriParams.algorithm;
} else {
throw new TypeError("Invalid 'algorithm' parameter");
}
}
// Digits: optional
if (typeof uriParams.digits !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.digits)) {
config.digits = parseInt(uriParams.digits, 10);
} else {
throw new TypeError("Invalid 'digits' parameter");
}
}
// HMAC: optional
if (typeof hmac !== "undefined") {
config.hmac = hmac;
}
return new OTP(config);
}
/**
* Converts an HOTP/TOTP object to a Google Authenticator key URI.
* @param {HOTP|TOTP} otp HOTP/TOTP object.
* @returns {string} Google Authenticator Key URI.
*/ static stringify(otp) {
if (otp instanceof HOTP || otp instanceof TOTP) {
return otp.toString();
}
throw new TypeError("Invalid 'HOTP/TOTP' object");
}
}
/**
* Library version.
* @type {string}
*/ const version = "9.5.0";
exports.HOTP = HOTP;
exports.Secret = Secret;
exports.TOTP = TOTP;
exports.URI = URI;
exports.version = version;

View File

@@ -0,0 +1,9 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
"use strict";function e(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:function(){return e[r]}})}}),t.default=e,Object.freeze(t)}var t=e(require("node:crypto"));const r=(()=>{if("object"==typeof globalThis)return globalThis;Object.defineProperty(Object.prototype,"__GLOBALTHIS__",{get(){return this},configurable:!0});try{if("undefined"!=typeof __GLOBALTHIS__)return __GLOBALTHIS__}finally{delete Object.prototype.__GLOBALTHIS__}return"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0})(),i=e=>{switch(!0){case/^(?:SHA-?1|SSL3-SHA1)$/i.test(e):return"SHA1";case/^SHA(?:2?-)?224$/i.test(e):return"SHA224";case/^SHA(?:2?-)?256$/i.test(e):return"SHA256";case/^SHA(?:2?-)?384$/i.test(e):return"SHA384";case/^SHA(?:2?-)?512$/i.test(e):return"SHA512";case/^SHA3-224$/i.test(e):return"SHA3-224";case/^SHA3-256$/i.test(e):return"SHA3-256";case/^SHA3-384$/i.test(e):return"SHA3-384";case/^SHA3-512$/i.test(e):return"SHA3-512";default:throw new TypeError(`Unknown hash algorithm: ${e}`)}},s=(e,i,s)=>{if(t?.createHmac){const n=t.createHmac(e,r.Buffer.from(i));return n.update(r.Buffer.from(s)),n.digest()}throw new Error("Missing HMAC function")},n="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",a=e=>{let t=(e=e.replace(/ /g,"")).length;for(;"="===e[t-1];)--t;e=(t<e.length?e.substring(0,t):e).toUpperCase();const r=new ArrayBuffer(5*e.length/8|0),i=new Uint8Array(r);let s=0,a=0,o=0;for(let t=0;t<e.length;t++){const r=n.indexOf(e[t]);if(-1===r)throw new TypeError(`Invalid character found: ${e[t]}`);a=a<<5|r,s+=5,s>=8&&(s-=8,i[o++]=a>>>s)}return i},o=e=>{let t=0,r=0,i="";for(let s=0;s<e.length;s++)for(r=r<<8|e[s],t+=8;t>=5;)i+=n[r>>>t-5&31],t-=5;return t>0&&(i+=n[r<<5-t&31]),i},l=e=>{e=e.replace(/ /g,"");const t=new ArrayBuffer(e.length/2),r=new Uint8Array(t);for(let t=0;t<e.length;t+=2)r[t/2]=parseInt(e.substring(t,t+2),16);return r},u=e=>{let t=""
;for(let r=0;r<e.length;r++){const i=e[r].toString(16);1===i.length&&(t+="0"),t+=i}return t.toUpperCase()},h=e=>{const t=new ArrayBuffer(e.length),r=new Uint8Array(t);for(let t=0;t<e.length;t++)r[t]=255&e.charCodeAt(t);return r},c=e=>{let t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t},d=r.TextEncoder?new r.TextEncoder:null,f=r.TextDecoder?new r.TextDecoder:null,g=e=>{if(!d)throw new Error("Encoding API not available");return d.encode(e)},m=e=>{if(!f)throw new Error("Encoding API not available");return f.decode(e)};class p{static fromLatin1(e){return new p({buffer:h(e).buffer})}static fromUTF8(e){return new p({buffer:g(e).buffer})}static fromBase32(e){return new p({buffer:a(e).buffer})}static fromHex(e){return new p({buffer:l(e).buffer})}get buffer(){return this.bytes.buffer}get latin1(){return Object.defineProperty(this,"latin1",{enumerable:!0,writable:!1,configurable:!1,value:c(this.bytes)}),this.latin1}get utf8(){return Object.defineProperty(this,"utf8",{enumerable:!0,writable:!1,configurable:!1,value:m(this.bytes)}),this.utf8}get base32(){return Object.defineProperty(this,"base32",{enumerable:!0,writable:!1,configurable:!1,value:o(this.bytes)}),this.base32}get hex(){return Object.defineProperty(this,"hex",{enumerable:!0,writable:!1,configurable:!1,value:u(this.bytes)}),this.hex}constructor({buffer:e,size:i=20}={}){this.bytes=void 0===e?(e=>{if(t?.randomBytes)return t.randomBytes(e);if(r.crypto?.getRandomValues)return r.crypto.getRandomValues(new Uint8Array(e));throw new Error("Cryptography API not available")})(i):new Uint8Array(e),Object.defineProperty(this,"bytes",{enumerable:!0,writable:!1,configurable:!1,value:this.bytes})}}class b{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,counter:0,window:1}}static generate({secret:e,algorithm:t=b.defaults.algorithm,digits:r=b.defaults.digits,counter:i=b.defaults.counter,hmac:n=s}){const a=(e=>{const t=new ArrayBuffer(8),r=new Uint8Array(t);let i=e;for(let e=7;e>=0&&0!==i;e--)r[e]=255&i,
i-=r[e],i/=256;return r})(i),o=n(t,e.bytes,a);if(!o?.byteLength||o.byteLength<19)throw new TypeError("Return value must be at least 19 bytes");const l=15&o[o.byteLength-1];return(((127&o[l])<<24|(255&o[l+1])<<16|(255&o[l+2])<<8|255&o[l+3])%10**r).toString().padStart(r,"0")}generate({counter:e=this.counter++}={}){return b.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:e,hmac:this.hmac})}static validate({token:e,secret:i,algorithm:n,digits:a=b.defaults.digits,counter:o=b.defaults.counter,window:l=b.defaults.window,hmac:u=s}){if(e.length!==a)return null;let h=null;const c=s=>{const l=b.generate({secret:i,algorithm:n,digits:a,counter:s,hmac:u});((e,i)=>{if(t?.timingSafeEqual)return t.timingSafeEqual(r.Buffer.from(e),r.Buffer.from(i));{if(e.length!==i.length)throw new TypeError("Input strings must have the same length");let t=-1,r=0;for(;++t<e.length;)r|=e.charCodeAt(t)^i.charCodeAt(t);return 0===r}})(e,l)&&(h=s-o)};c(o);for(let e=1;e<=l&&null===h&&(c(o-e),null===h)&&(c(o+e),null===h);++e);return h}validate({token:e,counter:t=this.counter,window:r}){return b.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://hotp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`counter=${e(this.counter)}`}constructor({issuer:e=b.defaults.issuer,label:t=b.defaults.label,issuerInLabel:r=b.defaults.issuerInLabel,secret:s=new p,algorithm:n=b.defaults.algorithm,digits:a=b.defaults.digits,counter:o=b.defaults.counter,hmac:l}={}){this.issuer=e,this.label=t,this.issuerInLabel=r,this.secret="string"==typeof s?p.fromBase32(s):s,this.algorithm=l?n:i(n),this.digits=a,this.counter=o,this.hmac=l}}class w{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,
algorithm:"SHA1",digits:6,period:30,window:1}}static counter({period:e=w.defaults.period,timestamp:t=Date.now()}={}){return Math.floor(t/1e3/e)}counter({timestamp:e=Date.now()}={}){return w.counter({period:this.period,timestamp:e})}static remaining({period:e=w.defaults.period,timestamp:t=Date.now()}={}){return 1e3*e-t%(1e3*e)}remaining({timestamp:e=Date.now()}={}){return w.remaining({period:this.period,timestamp:e})}static generate({secret:e,algorithm:t,digits:r,period:i=w.defaults.period,timestamp:s=Date.now(),hmac:n}){return b.generate({secret:e,algorithm:t,digits:r,counter:w.counter({period:i,timestamp:s}),hmac:n})}generate({timestamp:e=Date.now()}={}){return w.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:e,hmac:this.hmac})}static validate({token:e,secret:t,algorithm:r,digits:i,period:s=w.defaults.period,timestamp:n=Date.now(),window:a,hmac:o}){return b.validate({token:e,secret:t,algorithm:r,digits:i,counter:w.counter({period:s,timestamp:n}),window:a,hmac:o})}validate({token:e,timestamp:t,window:r}){return w.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://totp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`period=${e(this.period)}`}constructor({issuer:e=w.defaults.issuer,label:t=w.defaults.label,issuerInLabel:r=w.defaults.issuerInLabel,secret:s=new p,algorithm:n=w.defaults.algorithm,digits:a=w.defaults.digits,period:o=w.defaults.period,hmac:l}={}){this.issuer=e,this.label=t,this.issuerInLabel=r,this.secret="string"==typeof s?p.fromBase32(s):s,this.algorithm=l?n:i(n),this.digits=a,this.period=o,this.hmac=l}}
const y=/^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i,A=/^[2-7A-Z]+=*$/i,v=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,S=/^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i,I=/^[+-]?\d+$/,$=/^\+?[1-9]\d*$/;exports.HOTP=b,exports.Secret=p,exports.TOTP=w,exports.URI=class{static parse(e,{hmac:t}={}){let r;try{r=e.match(y)}catch(e){}if(!Array.isArray(r))throw new URIError("Invalid URI format");const i=r[1].toLowerCase(),s=r[2].split(/(?::|%3A) *(.+)/i,2).map(decodeURIComponent),n=r[3].split("&").reduce((e,t)=>{const r=t.split(/=(.*)/,2).map(decodeURIComponent),i=r[0].toLowerCase(),s=r[1],n=e;return n[i]=s,n},{});let a;const o={};if("hotp"===i){if(a=b,void 0===n.counter||!I.test(n.counter))throw new TypeError("Missing or invalid 'counter' parameter");o.counter=parseInt(n.counter,10)}else{if("totp"!==i)throw new TypeError("Unknown OTP type");if(a=w,void 0!==n.period){if(!$.test(n.period))throw new TypeError("Invalid 'period' parameter");o.period=parseInt(n.period,10)}}if(void 0!==n.issuer&&(o.issuer=n.issuer),2===s.length?(o.label=s[1],void 0===o.issuer||""===o.issuer?o.issuer=s[0]:""===s[0]&&(o.issuerInLabel=!1)):(o.label=s[0],void 0!==o.issuer&&""!==o.issuer&&(o.issuerInLabel=!1)),void 0===n.secret||!A.test(n.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(o.secret=n.secret,void 0!==n.algorithm){if(!(t?S:v).test(n.algorithm))throw new TypeError("Invalid 'algorithm' parameter");o.algorithm=n.algorithm}if(void 0!==n.digits){if(!$.test(n.digits))throw new TypeError("Invalid 'digits' parameter");o.digits=parseInt(n.digits,10)}return void 0!==t&&(o.hmac=t),new a(o)}static stringify(e){if(e instanceof b||e instanceof w)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}},exports.version="9.5.0";
//# sourceMappingURL=otpauth.node.min.cjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
import*as e from"node:crypto";const t=(()=>{if("object"==typeof globalThis)return globalThis;Object.defineProperty(Object.prototype,"__GLOBALTHIS__",{get(){return this},configurable:!0});try{if("undefined"!=typeof __GLOBALTHIS__)return __GLOBALTHIS__}finally{delete Object.prototype.__GLOBALTHIS__}return"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0})(),r=e=>{switch(!0){case/^(?:SHA-?1|SSL3-SHA1)$/i.test(e):return"SHA1";case/^SHA(?:2?-)?224$/i.test(e):return"SHA224";case/^SHA(?:2?-)?256$/i.test(e):return"SHA256";case/^SHA(?:2?-)?384$/i.test(e):return"SHA384";case/^SHA(?:2?-)?512$/i.test(e):return"SHA512";case/^SHA3-224$/i.test(e):return"SHA3-224";case/^SHA3-256$/i.test(e):return"SHA3-256";case/^SHA3-384$/i.test(e):return"SHA3-384";case/^SHA3-512$/i.test(e):return"SHA3-512";default:throw new TypeError(`Unknown hash algorithm: ${e}`)}},i=(r,i,s)=>{if(e?.createHmac){const n=e.createHmac(r,t.Buffer.from(i));return n.update(t.Buffer.from(s)),n.digest()}throw new Error("Missing HMAC function")},s="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",n=e=>{let t=(e=e.replace(/ /g,"")).length;for(;"="===e[t-1];)--t;e=(t<e.length?e.substring(0,t):e).toUpperCase();const r=new ArrayBuffer(5*e.length/8|0),i=new Uint8Array(r);let n=0,a=0,o=0;for(let t=0;t<e.length;t++){const r=s.indexOf(e[t]);if(-1===r)throw new TypeError(`Invalid character found: ${e[t]}`);a=a<<5|r,n+=5,n>=8&&(n-=8,i[o++]=a>>>n)}return i},a=e=>{let t=0,r=0,i="";for(let n=0;n<e.length;n++)for(r=r<<8|e[n],t+=8;t>=5;)i+=s[r>>>t-5&31],t-=5;return t>0&&(i+=s[r<<5-t&31]),i},o=e=>{e=e.replace(/ /g,"");const t=new ArrayBuffer(e.length/2),r=new Uint8Array(t);for(let t=0;t<e.length;t+=2)r[t/2]=parseInt(e.substring(t,t+2),16);return r},l=e=>{let t="";for(let r=0;r<e.length;r++){const i=e[r].toString(16);1===i.length&&(t+="0"),t+=i}return t.toUpperCase()},u=e=>{const t=new ArrayBuffer(e.length),r=new Uint8Array(t);for(let t=0;t<e.length;t++)r[t]=255&e.charCodeAt(t);return r},h=e=>{let t=""
;for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t},c=t.TextEncoder?new t.TextEncoder:null,d=t.TextDecoder?new t.TextDecoder:null,f=e=>{if(!c)throw new Error("Encoding API not available");return c.encode(e)},g=e=>{if(!d)throw new Error("Encoding API not available");return d.decode(e)};class m{static fromLatin1(e){return new m({buffer:u(e).buffer})}static fromUTF8(e){return new m({buffer:f(e).buffer})}static fromBase32(e){return new m({buffer:n(e).buffer})}static fromHex(e){return new m({buffer:o(e).buffer})}get buffer(){return this.bytes.buffer}get latin1(){return Object.defineProperty(this,"latin1",{enumerable:!0,writable:!1,configurable:!1,value:h(this.bytes)}),this.latin1}get utf8(){return Object.defineProperty(this,"utf8",{enumerable:!0,writable:!1,configurable:!1,value:g(this.bytes)}),this.utf8}get base32(){return Object.defineProperty(this,"base32",{enumerable:!0,writable:!1,configurable:!1,value:a(this.bytes)}),this.base32}get hex(){return Object.defineProperty(this,"hex",{enumerable:!0,writable:!1,configurable:!1,value:l(this.bytes)}),this.hex}constructor({buffer:r,size:i=20}={}){this.bytes=void 0===r?(r=>{if(e?.randomBytes)return e.randomBytes(r);if(t.crypto?.getRandomValues)return t.crypto.getRandomValues(new Uint8Array(r));throw new Error("Cryptography API not available")})(i):new Uint8Array(r),Object.defineProperty(this,"bytes",{enumerable:!0,writable:!1,configurable:!1,value:this.bytes})}}class p{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,counter:0,window:1}}static generate({secret:e,algorithm:t=p.defaults.algorithm,digits:r=p.defaults.digits,counter:s=p.defaults.counter,hmac:n=i}){const a=(e=>{const t=new ArrayBuffer(8),r=new Uint8Array(t);let i=e;for(let e=7;e>=0&&0!==i;e--)r[e]=255&i,i-=r[e],i/=256;return r})(s),o=n(t,e.bytes,a);if(!o?.byteLength||o.byteLength<19)throw new TypeError("Return value must be at least 19 bytes");const l=15&o[o.byteLength-1]
;return(((127&o[l])<<24|(255&o[l+1])<<16|(255&o[l+2])<<8|255&o[l+3])%10**r).toString().padStart(r,"0")}generate({counter:e=this.counter++}={}){return p.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:e,hmac:this.hmac})}static validate({token:r,secret:s,algorithm:n,digits:a=p.defaults.digits,counter:o=p.defaults.counter,window:l=p.defaults.window,hmac:u=i}){if(r.length!==a)return null;let h=null;const c=i=>{const l=p.generate({secret:s,algorithm:n,digits:a,counter:i,hmac:u});((r,i)=>{if(e?.timingSafeEqual)return e.timingSafeEqual(t.Buffer.from(r),t.Buffer.from(i));{if(r.length!==i.length)throw new TypeError("Input strings must have the same length");let e=-1,t=0;for(;++e<r.length;)t|=r.charCodeAt(e)^i.charCodeAt(e);return 0===t}})(r,l)&&(h=i-o)};c(o);for(let e=1;e<=l&&null===h&&(c(o-e),null===h)&&(c(o+e),null===h);++e);return h}validate({token:e,counter:t=this.counter,window:r}){return p.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://hotp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`counter=${e(this.counter)}`}constructor({issuer:e=p.defaults.issuer,label:t=p.defaults.label,issuerInLabel:i=p.defaults.issuerInLabel,secret:s=new m,algorithm:n=p.defaults.algorithm,digits:a=p.defaults.digits,counter:o=p.defaults.counter,hmac:l}={}){this.issuer=e,this.label=t,this.issuerInLabel=i,this.secret="string"==typeof s?m.fromBase32(s):s,this.algorithm=l?n:r(n),this.digits=a,this.counter=o,this.hmac=l}}class b{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,period:30,window:1}}static counter({period:e=b.defaults.period,timestamp:t=Date.now()}={}){return Math.floor(t/1e3/e)}
counter({timestamp:e=Date.now()}={}){return b.counter({period:this.period,timestamp:e})}static remaining({period:e=b.defaults.period,timestamp:t=Date.now()}={}){return 1e3*e-t%(1e3*e)}remaining({timestamp:e=Date.now()}={}){return b.remaining({period:this.period,timestamp:e})}static generate({secret:e,algorithm:t,digits:r,period:i=b.defaults.period,timestamp:s=Date.now(),hmac:n}){return p.generate({secret:e,algorithm:t,digits:r,counter:b.counter({period:i,timestamp:s}),hmac:n})}generate({timestamp:e=Date.now()}={}){return b.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:e,hmac:this.hmac})}static validate({token:e,secret:t,algorithm:r,digits:i,period:s=b.defaults.period,timestamp:n=Date.now(),window:a,hmac:o}){return p.validate({token:e,secret:t,algorithm:r,digits:i,counter:b.counter({period:s,timestamp:n}),window:a,hmac:o})}validate({token:e,timestamp:t,window:r}){return b.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://totp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`period=${e(this.period)}`}constructor({issuer:e=b.defaults.issuer,label:t=b.defaults.label,issuerInLabel:i=b.defaults.issuerInLabel,secret:s=new m,algorithm:n=b.defaults.algorithm,digits:a=b.defaults.digits,period:o=b.defaults.period,hmac:l}={}){this.issuer=e,this.label=t,this.issuerInLabel=i,this.secret="string"==typeof s?m.fromBase32(s):s,this.algorithm=l?n:r(n),this.digits=a,this.period=o,this.hmac=l}}
const w=/^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i,y=/^[2-7A-Z]+=*$/i,A=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,S=/^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i,I=/^[+-]?\d+$/,$=/^\+?[1-9]\d*$/;class v{static parse(e,{hmac:t}={}){let r;try{r=e.match(w)}catch(e){}if(!Array.isArray(r))throw new URIError("Invalid URI format");const i=r[1].toLowerCase(),s=r[2].split(/(?::|%3A) *(.+)/i,2).map(decodeURIComponent),n=r[3].split("&").reduce((e,t)=>{const r=t.split(/=(.*)/,2).map(decodeURIComponent),i=r[0].toLowerCase(),s=r[1],n=e;return n[i]=s,n},{});let a;const o={};if("hotp"===i){if(a=p,void 0===n.counter||!I.test(n.counter))throw new TypeError("Missing or invalid 'counter' parameter");o.counter=parseInt(n.counter,10)}else{if("totp"!==i)throw new TypeError("Unknown OTP type");if(a=b,void 0!==n.period){if(!$.test(n.period))throw new TypeError("Invalid 'period' parameter");o.period=parseInt(n.period,10)}}if(void 0!==n.issuer&&(o.issuer=n.issuer),2===s.length?(o.label=s[1],void 0===o.issuer||""===o.issuer?o.issuer=s[0]:""===s[0]&&(o.issuerInLabel=!1)):(o.label=s[0],void 0!==o.issuer&&""!==o.issuer&&(o.issuerInLabel=!1)),void 0===n.secret||!y.test(n.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(o.secret=n.secret,void 0!==n.algorithm){if(!(t?S:A).test(n.algorithm))throw new TypeError("Invalid 'algorithm' parameter");o.algorithm=n.algorithm}if(void 0!==n.digits){if(!$.test(n.digits))throw new TypeError("Invalid 'digits' parameter");o.digits=parseInt(n.digits,10)}return void 0!==t&&(o.hmac=t),new a(o)}static stringify(e){if(e instanceof p||e instanceof b)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}const H="9.5.0";export{p as HOTP,m as Secret,b as TOTP,v as URI,H as version};
//# sourceMappingURL=otpauth.node.min.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,894 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
import * as crypto from 'node:crypto';
/**
* Converts an integer to an Uint8Array.
* @param {number} num Integer.
* @returns {Uint8Array} Uint8Array.
*/ const uintDecode = (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 arr;
};
/**
* "globalThis" ponyfill.
* @see [A horrifying globalThis polyfill in universal JavaScript](https://mathiasbynens.be/notes/globalthis)
* @type {Object.<string, *>}
*/ const globalScope = (()=>{
if (typeof globalThis === "object") return globalThis;
else {
Object.defineProperty(Object.prototype, "__GLOBALTHIS__", {
get () {
return this;
},
configurable: true
});
try {
// @ts-expect-error
// eslint-disable-next-line no-undef
if (typeof __GLOBALTHIS__ !== "undefined") return __GLOBALTHIS__;
} finally{
// @ts-expect-error
delete Object.prototype.__GLOBALTHIS__;
}
}
// Still unable to determine "globalThis", fall back to a naive method.
if (typeof self !== "undefined") return self;
else if (typeof window !== "undefined") return window;
else if (typeof global !== "undefined") return global;
return undefined;
})();
/**
* Canonicalizes a hash algorithm name.
* @param {string} algorithm Hash algorithm name.
* @returns {"SHA1"|"SHA224"|"SHA256"|"SHA384"|"SHA512"|"SHA3-224"|"SHA3-256"|"SHA3-384"|"SHA3-512"} Canonicalized hash algorithm name.
*/ const canonicalizeAlgorithm = (algorithm)=>{
switch(true){
case /^(?:SHA-?1|SSL3-SHA1)$/i.test(algorithm):
return "SHA1";
case /^SHA(?:2?-)?224$/i.test(algorithm):
return "SHA224";
case /^SHA(?:2?-)?256$/i.test(algorithm):
return "SHA256";
case /^SHA(?:2?-)?384$/i.test(algorithm):
return "SHA384";
case /^SHA(?:2?-)?512$/i.test(algorithm):
return "SHA512";
case /^SHA3-224$/i.test(algorithm):
return "SHA3-224";
case /^SHA3-256$/i.test(algorithm):
return "SHA3-256";
case /^SHA3-384$/i.test(algorithm):
return "SHA3-384";
case /^SHA3-512$/i.test(algorithm):
return "SHA3-512";
default:
throw new TypeError(`Unknown hash algorithm: ${algorithm}`);
}
};
/**
* Calculates an HMAC digest.
* @param {string} algorithm Algorithm.
* @param {Uint8Array} key Key.
* @param {Uint8Array} message Message.
* @returns {Uint8Array} Digest.
*/ const hmacDigest = (algorithm, key, message)=>{
if (crypto?.createHmac) {
const hmac = crypto.createHmac(algorithm, globalScope.Buffer.from(key));
hmac.update(globalScope.Buffer.from(message));
return hmac.digest();
} else {
throw new Error("Missing HMAC function");
}
};
/**
* RFC 4648 base32 alphabet without pad.
* @type {string}
*/ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
/**
* Converts a base32 string to an Uint8Array (RFC 4648).
* @see [LinusU/base32-decode](https://github.com/LinusU/base32-decode)
* @param {string} str Base32 string.
* @returns {Uint8Array} Uint8Array.
*/ const base32Decode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
// Canonicalize to all upper case and remove padding if it exists.
let end = str.length;
while(str[end - 1] === "=")--end;
str = (end < str.length ? str.substring(0, end) : str).toUpperCase();
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 = ALPHABET.indexOf(str[i]);
if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`);
value = value << 5 | idx;
bits += 5;
if (bits >= 8) {
bits -= 8;
arr[index++] = value >>> bits;
}
}
return arr;
};
/**
* Converts an Uint8Array to a base32 string (RFC 4648).
* @see [LinusU/base32-encode](https://github.com/LinusU/base32-encode)
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Base32 string.
*/ const base32Encode = (arr)=>{
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 += ALPHABET[value >>> bits - 5 & 31];
bits -= 5;
}
}
if (bits > 0) {
str += ALPHABET[value << 5 - bits & 31];
}
return str;
};
/**
* Converts a hexadecimal string to an Uint8Array.
* @param {string} str Hexadecimal string.
* @returns {Uint8Array} Uint8Array.
*/ const hexDecode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
const buf = new ArrayBuffer(str.length / 2);
const arr = new Uint8Array(buf);
for(let i = 0; i < str.length; i += 2){
arr[i / 2] = parseInt(str.substring(i, i + 2), 16);
}
return arr;
};
/**
* Converts an Uint8Array to a hexadecimal string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Hexadecimal string.
*/ const hexEncode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
const hex = arr[i].toString(16);
if (hex.length === 1) str += "0";
str += hex;
}
return str.toUpperCase();
};
/**
* Converts a Latin-1 string to an Uint8Array.
* @param {string} str Latin-1 string.
* @returns {Uint8Array} Uint8Array.
*/ const latin1Decode = (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) & 0xff;
}
return arr;
};
/**
* Converts an Uint8Array to a Latin-1 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Latin-1 string.
*/ const latin1Encode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
str += String.fromCharCode(arr[i]);
}
return str;
};
/**
* TextEncoder instance.
* @type {TextEncoder|null}
*/ const ENCODER = globalScope.TextEncoder ? new globalScope.TextEncoder() : null;
/**
* TextDecoder instance.
* @type {TextDecoder|null}
*/ const DECODER = globalScope.TextDecoder ? new globalScope.TextDecoder() : null;
/**
* Converts an UTF-8 string to an Uint8Array.
* @param {string} str String.
* @returns {Uint8Array} Uint8Array.
*/ const utf8Decode = (str)=>{
if (!ENCODER) {
throw new Error("Encoding API not available");
}
return ENCODER.encode(str);
};
/**
* Converts an Uint8Array to an UTF-8 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} String.
*/ const utf8Encode = (arr)=>{
if (!DECODER) {
throw new Error("Encoding API not available");
}
return DECODER.decode(arr);
};
/**
* Returns random bytes.
* @param {number} size Size.
* @returns {Uint8Array} Random bytes.
*/ const randomBytes = (size)=>{
if (crypto?.randomBytes) {
return crypto.randomBytes(size);
} else if (globalScope.crypto?.getRandomValues) {
return globalScope.crypto.getRandomValues(new Uint8Array(size));
} else {
throw new Error("Cryptography API not available");
}
};
/**
* OTP secret key.
*/ class Secret {
/**
* Converts a Latin-1 string to a Secret object.
* @param {string} str Latin-1 string.
* @returns {Secret} Secret object.
*/ static fromLatin1(str) {
return new Secret({
buffer: latin1Decode(str).buffer
});
}
/**
* Converts an UTF-8 string to a Secret object.
* @param {string} str UTF-8 string.
* @returns {Secret} Secret object.
*/ static fromUTF8(str) {
return new Secret({
buffer: utf8Decode(str).buffer
});
}
/**
* Converts a base32 string to a Secret object.
* @param {string} str Base32 string.
* @returns {Secret} Secret object.
*/ static fromBase32(str) {
return new Secret({
buffer: base32Decode(str).buffer
});
}
/**
* Converts a hexadecimal string to a Secret object.
* @param {string} str Hexadecimal string.
* @returns {Secret} Secret object.
*/ static fromHex(str) {
return new Secret({
buffer: hexDecode(str).buffer
});
}
/**
* Secret key buffer.
* @deprecated For backward compatibility, the "bytes" property should be used instead.
* @type {ArrayBufferLike}
*/ get buffer() {
return this.bytes.buffer;
}
/**
* Latin-1 string representation of secret key.
* @type {string}
*/ get latin1() {
Object.defineProperty(this, "latin1", {
enumerable: true,
writable: false,
configurable: false,
value: latin1Encode(this.bytes)
});
return this.latin1;
}
/**
* UTF-8 string representation of secret key.
* @type {string}
*/ get utf8() {
Object.defineProperty(this, "utf8", {
enumerable: true,
writable: false,
configurable: false,
value: utf8Encode(this.bytes)
});
return this.utf8;
}
/**
* Base32 string representation of secret key.
* @type {string}
*/ get base32() {
Object.defineProperty(this, "base32", {
enumerable: true,
writable: false,
configurable: false,
value: base32Encode(this.bytes)
});
return this.base32;
}
/**
* Hexadecimal string representation of secret key.
* @type {string}
*/ get hex() {
Object.defineProperty(this, "hex", {
enumerable: true,
writable: false,
configurable: false,
value: hexEncode(this.bytes)
});
return this.hex;
}
/**
* Creates a secret key object.
* @param {Object} [config] Configuration options.
* @param {ArrayBufferLike} [config.buffer] Secret key buffer.
* @param {number} [config.size=20] Number of random bytes to generate, ignored if 'buffer' is provided.
*/ constructor({ buffer, size = 20 } = {}){
/**
* Secret key.
* @type {Uint8Array}
* @readonly
*/ this.bytes = typeof buffer === "undefined" ? randomBytes(size) : new Uint8Array(buffer);
// Prevent the "bytes" property from being modified.
Object.defineProperty(this, "bytes", {
enumerable: true,
writable: false,
configurable: false,
value: this.bytes
});
}
}
/**
* Returns true if a is equal to b, without leaking timing information that would allow an attacker to guess one of the values.
* @param {string} a String a.
* @param {string} b String b.
* @returns {boolean} Equality result.
*/ const timingSafeEqual = (a, b)=>{
if (crypto?.timingSafeEqual) {
return crypto.timingSafeEqual(globalScope.Buffer.from(a), globalScope.Buffer.from(b));
} else {
if (a.length !== b.length) {
throw new TypeError("Input strings must have the same length");
}
let i = -1;
let out = 0;
while(++i < a.length){
out |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return out === 0;
}
};
/**
* HOTP: An HMAC-based One-time Password Algorithm.
* @see [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
*/ class HOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* counter: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
counter: 0,
window: 1
};
}
/**
* Generates an HOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac = hmacDigest }) {
const message = uintDecode(counter);
const digest = hmac(algorithm, secret.bytes, message);
if (!digest?.byteLength || digest.byteLength < 19) {
throw new TypeError("Return value must be at least 19 bytes");
}
const offset = digest[digest.byteLength - 1] & 15;
const otp = ((digest[offset] & 127) << 24 | (digest[offset + 1] & 255) << 16 | (digest[offset + 2] & 255) << 8 | digest[offset + 3] & 255) % 10 ** digits;
return otp.toString().padStart(digits, "0");
}
/**
* Generates an HOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.counter=this.counter++] Counter value.
* @returns {string} Token.
*/ generate({ counter = this.counter++ } = {}) {
return HOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
hmac: this.hmac
});
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, window = HOTP.defaults.window, hmac = hmacDigest }) {
// Return early if the token length does not match the digit number.
if (token.length !== digits) return null;
let delta = null;
const check = (/** @type {number} */ i)=>{
const generatedToken = HOTP.generate({
secret,
algorithm,
digits,
counter: i,
hmac
});
if (timingSafeEqual(token, generatedToken)) {
delta = i - counter;
}
};
check(counter);
for(let i = 1; i <= window && delta === null; ++i){
check(counter - i);
if (delta !== null) break;
check(counter + i);
if (delta !== null) break;
}
return delta;
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.counter=this.counter] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, counter = this.counter, window }) {
return HOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://hotp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `counter=${e(this.counter)}`;
}
/**
* Creates an HOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Initial counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = HOTP.defaults.issuer, label = HOTP.defaults.label, issuerInLabel = HOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Initial counter value.
* @type {number}
*/ this.counter = counter;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
*/ class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
period: 30,
window: 1
};
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ static counter({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return Math.floor(timestamp / 1000 / period);
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ counter({ timestamp = Date.now() } = {}) {
return TOTP.counter({
period: this.period,
timestamp
});
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ static remaining({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return period * 1000 - timestamp % (period * 1000);
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ remaining({ timestamp = Date.now() } = {}) {
return TOTP.remaining({
period: this.period,
timestamp
});
}
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), hmac }) {
return HOTP.generate({
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
hmac
});
}
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/ generate({ timestamp = Date.now() } = {}) {
return TOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
hmac: this.hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), window, hmac }) {
return HOTP.validate({
token,
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
window,
hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, timestamp, window }) {
return TOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://totp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `period=${e(this.period)}`;
}
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = TOTP.defaults.issuer, label = TOTP.defaults.label, issuerInLabel = TOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = TOTP.defaults.algorithm, digits = TOTP.defaults.digits, period = TOTP.defaults.period, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Token time-step duration.
* @type {number}
*/ this.period = period;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* Key URI regex (otpauth://TYPE/[ISSUER:]LABEL?PARAMETERS).
* @type {RegExp}
*/ const OTPURI_REGEX = /^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i;
/**
* RFC 4648 base32 alphabet with pad.
* @type {RegExp}
*/ const SECRET_REGEX = /^[2-7A-Z]+=*$/i;
/**
* Regex for supported algorithms in built-in HMAC function.
* @type {RegExp}
*/ const ALGORITHM_REGEX = /^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i;
/**
* Regex for custom algorithms in user-defined HMAC function.
* @type {RegExp}
*/ const ALGORITHM_CUSTOM_REGEX = /^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i;
/**
* Integer regex.
* @type {RegExp}
*/ const INTEGER_REGEX = /^[+-]?\d+$/;
/**
* Positive integer regex.
* @type {RegExp}
*/ const POSITIVE_INTEGER_REGEX = /^\+?[1-9]\d*$/;
/**
* HOTP/TOTP object/string conversion.
* @see [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
*/ class URI {
/**
* Parses a Google Authenticator key URI and returns an HOTP/TOTP object.
* @param {string} uri Google Authenticator Key URI.
* @param {Object} [config] Configuration options.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/ static parse(uri, { hmac } = {}) {
let uriGroups;
try {
uriGroups = uri.match(OTPURI_REGEX);
// eslint-disable-next-line no-unused-vars
} catch (_) {
/* Handled below */ }
if (!Array.isArray(uriGroups)) {
throw new URIError("Invalid URI format");
}
// Extract URI groups.
const uriType = uriGroups[1].toLowerCase();
const uriLabel = uriGroups[2].split(/(?::|%3A) *(.+)/i, 2).map(decodeURIComponent);
/** @type {Object.<string, string>} */ const uriParams = uriGroups[3].split("&").reduce((acc, cur)=>{
const pairArr = cur.split(/=(.*)/, 2).map(decodeURIComponent);
const pairKey = pairArr[0].toLowerCase();
const pairVal = pairArr[1];
/** @type {Object.<string, string>} */ const pairAcc = acc;
pairAcc[pairKey] = pairVal;
return pairAcc;
}, {});
// 'OTP' will be instantiated with 'config' argument.
let OTP;
const config = {};
if (uriType === "hotp") {
OTP = HOTP;
// Counter: required
if (typeof uriParams.counter !== "undefined" && INTEGER_REGEX.test(uriParams.counter)) {
config.counter = parseInt(uriParams.counter, 10);
} else {
throw new TypeError("Missing or invalid 'counter' parameter");
}
} else if (uriType === "totp") {
OTP = TOTP;
// Period: optional
if (typeof uriParams.period !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.period)) {
config.period = parseInt(uriParams.period, 10);
} else {
throw new TypeError("Invalid 'period' parameter");
}
}
} else {
throw new TypeError("Unknown OTP type");
}
// Label: required
// Issuer: optional
if (typeof uriParams.issuer !== "undefined") {
config.issuer = uriParams.issuer;
}
if (uriLabel.length === 2) {
config.label = uriLabel[1];
if (typeof config.issuer === "undefined" || config.issuer === "") {
config.issuer = uriLabel[0];
} else if (uriLabel[0] === "") {
config.issuerInLabel = false;
}
} else {
config.label = uriLabel[0];
if (typeof config.issuer !== "undefined" && config.issuer !== "") {
config.issuerInLabel = false;
}
}
// Secret: required
if (typeof uriParams.secret !== "undefined" && SECRET_REGEX.test(uriParams.secret)) {
config.secret = uriParams.secret;
} else {
throw new TypeError("Missing or invalid 'secret' parameter");
}
// Algorithm: optional
if (typeof uriParams.algorithm !== "undefined") {
if ((hmac ? ALGORITHM_CUSTOM_REGEX : ALGORITHM_REGEX).test(uriParams.algorithm)) {
config.algorithm = uriParams.algorithm;
} else {
throw new TypeError("Invalid 'algorithm' parameter");
}
}
// Digits: optional
if (typeof uriParams.digits !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.digits)) {
config.digits = parseInt(uriParams.digits, 10);
} else {
throw new TypeError("Invalid 'digits' parameter");
}
}
// HMAC: optional
if (typeof hmac !== "undefined") {
config.hmac = hmac;
}
return new OTP(config);
}
/**
* Converts an HOTP/TOTP object to a Google Authenticator key URI.
* @param {HOTP|TOTP} otp HOTP/TOTP object.
* @returns {string} Google Authenticator Key URI.
*/ static stringify(otp) {
if (otp instanceof HOTP || otp instanceof TOTP) {
return otp.toString();
}
throw new TypeError("Invalid 'HOTP/TOTP' object");
}
}
/**
* Library version.
* @type {string}
*/ const version = "9.5.0";
export { HOTP, Secret, TOTP, URI, version };

View File

@@ -0,0 +1,907 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
//! noble-hashes 2.0.1 | (c) Paul Miller | MIT | https://github.com/paulmillr/noble-hashes
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
import { hmac } from '@noble/hashes/hmac.js';
import { sha1 } from '@noble/hashes/legacy.js';
import { sha512, sha384, sha256, sha224 } from '@noble/hashes/sha2.js';
import { sha3_512, sha3_384, sha3_256, sha3_224 } from '@noble/hashes/sha3.js';
/**
* Converts an integer to an Uint8Array.
* @param {number} num Integer.
* @returns {Uint8Array} Uint8Array.
*/ const uintDecode = (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 arr;
};
/**
* "globalThis" ponyfill.
* @see [A horrifying globalThis polyfill in universal JavaScript](https://mathiasbynens.be/notes/globalthis)
* @type {Object.<string, *>}
*/ const globalScope = (()=>{
if (typeof globalThis === "object") return globalThis;
else {
Object.defineProperty(Object.prototype, "__GLOBALTHIS__", {
get () {
return this;
},
configurable: true
});
try {
// @ts-expect-error
// eslint-disable-next-line no-undef
if (typeof __GLOBALTHIS__ !== "undefined") return __GLOBALTHIS__;
} finally{
// @ts-expect-error
delete Object.prototype.__GLOBALTHIS__;
}
}
// Still unable to determine "globalThis", fall back to a naive method.
if (typeof self !== "undefined") return self;
else if (typeof window !== "undefined") return window;
else if (typeof global !== "undefined") return global;
return undefined;
})();
/**
* @noble/hashes hash functions.
* @type {Object.<string, sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512>}
*/ const nobleHashes = {
SHA1: sha1,
SHA224: sha224,
SHA256: sha256,
SHA384: sha384,
SHA512: sha512,
"SHA3-224": sha3_224,
"SHA3-256": sha3_256,
"SHA3-384": sha3_384,
"SHA3-512": sha3_512
};
/**
* Canonicalizes a hash algorithm name.
* @param {string} algorithm Hash algorithm name.
* @returns {"SHA1"|"SHA224"|"SHA256"|"SHA384"|"SHA512"|"SHA3-224"|"SHA3-256"|"SHA3-384"|"SHA3-512"} Canonicalized hash algorithm name.
*/ const canonicalizeAlgorithm = (algorithm)=>{
switch(true){
case /^(?:SHA-?1|SSL3-SHA1)$/i.test(algorithm):
return "SHA1";
case /^SHA(?:2?-)?224$/i.test(algorithm):
return "SHA224";
case /^SHA(?:2?-)?256$/i.test(algorithm):
return "SHA256";
case /^SHA(?:2?-)?384$/i.test(algorithm):
return "SHA384";
case /^SHA(?:2?-)?512$/i.test(algorithm):
return "SHA512";
case /^SHA3-224$/i.test(algorithm):
return "SHA3-224";
case /^SHA3-256$/i.test(algorithm):
return "SHA3-256";
case /^SHA3-384$/i.test(algorithm):
return "SHA3-384";
case /^SHA3-512$/i.test(algorithm):
return "SHA3-512";
default:
throw new TypeError(`Unknown hash algorithm: ${algorithm}`);
}
};
/**
* Calculates an HMAC digest.
* @param {string} algorithm Algorithm.
* @param {Uint8Array} key Key.
* @param {Uint8Array} message Message.
* @returns {Uint8Array} Digest.
*/ const hmacDigest = (algorithm, key, message)=>{
if (hmac) {
const hash = nobleHashes[algorithm] ?? nobleHashes[canonicalizeAlgorithm(algorithm)];
return hmac(hash, key, message);
} else {
throw new Error("Missing HMAC function");
}
};
/**
* RFC 4648 base32 alphabet without pad.
* @type {string}
*/ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
/**
* Converts a base32 string to an Uint8Array (RFC 4648).
* @see [LinusU/base32-decode](https://github.com/LinusU/base32-decode)
* @param {string} str Base32 string.
* @returns {Uint8Array} Uint8Array.
*/ const base32Decode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
// Canonicalize to all upper case and remove padding if it exists.
let end = str.length;
while(str[end - 1] === "=")--end;
str = (end < str.length ? str.substring(0, end) : str).toUpperCase();
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 = ALPHABET.indexOf(str[i]);
if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`);
value = value << 5 | idx;
bits += 5;
if (bits >= 8) {
bits -= 8;
arr[index++] = value >>> bits;
}
}
return arr;
};
/**
* Converts an Uint8Array to a base32 string (RFC 4648).
* @see [LinusU/base32-encode](https://github.com/LinusU/base32-encode)
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Base32 string.
*/ const base32Encode = (arr)=>{
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 += ALPHABET[value >>> bits - 5 & 31];
bits -= 5;
}
}
if (bits > 0) {
str += ALPHABET[value << 5 - bits & 31];
}
return str;
};
/**
* Converts a hexadecimal string to an Uint8Array.
* @param {string} str Hexadecimal string.
* @returns {Uint8Array} Uint8Array.
*/ const hexDecode = (str)=>{
// Remove spaces (although they are not allowed by the spec, some issuers add them for readability).
str = str.replace(/ /g, "");
const buf = new ArrayBuffer(str.length / 2);
const arr = new Uint8Array(buf);
for(let i = 0; i < str.length; i += 2){
arr[i / 2] = parseInt(str.substring(i, i + 2), 16);
}
return arr;
};
/**
* Converts an Uint8Array to a hexadecimal string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Hexadecimal string.
*/ const hexEncode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
const hex = arr[i].toString(16);
if (hex.length === 1) str += "0";
str += hex;
}
return str.toUpperCase();
};
/**
* Converts a Latin-1 string to an Uint8Array.
* @param {string} str Latin-1 string.
* @returns {Uint8Array} Uint8Array.
*/ const latin1Decode = (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) & 0xff;
}
return arr;
};
/**
* Converts an Uint8Array to a Latin-1 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} Latin-1 string.
*/ const latin1Encode = (arr)=>{
let str = "";
for(let i = 0; i < arr.length; i++){
str += String.fromCharCode(arr[i]);
}
return str;
};
/**
* TextEncoder instance.
* @type {TextEncoder|null}
*/ const ENCODER = globalScope.TextEncoder ? new globalScope.TextEncoder() : null;
/**
* TextDecoder instance.
* @type {TextDecoder|null}
*/ const DECODER = globalScope.TextDecoder ? new globalScope.TextDecoder() : null;
/**
* Converts an UTF-8 string to an Uint8Array.
* @param {string} str String.
* @returns {Uint8Array} Uint8Array.
*/ const utf8Decode = (str)=>{
if (!ENCODER) {
throw new Error("Encoding API not available");
}
return ENCODER.encode(str);
};
/**
* Converts an Uint8Array to an UTF-8 string.
* @param {Uint8Array} arr Uint8Array.
* @returns {string} String.
*/ const utf8Encode = (arr)=>{
if (!DECODER) {
throw new Error("Encoding API not available");
}
return DECODER.decode(arr);
};
/**
* Returns random bytes.
* @param {number} size Size.
* @returns {Uint8Array} Random bytes.
*/ const randomBytes = (size)=>{
if (globalScope.crypto?.getRandomValues) {
return globalScope.crypto.getRandomValues(new Uint8Array(size));
} else {
throw new Error("Cryptography API not available");
}
};
/**
* OTP secret key.
*/ class Secret {
/**
* Converts a Latin-1 string to a Secret object.
* @param {string} str Latin-1 string.
* @returns {Secret} Secret object.
*/ static fromLatin1(str) {
return new Secret({
buffer: latin1Decode(str).buffer
});
}
/**
* Converts an UTF-8 string to a Secret object.
* @param {string} str UTF-8 string.
* @returns {Secret} Secret object.
*/ static fromUTF8(str) {
return new Secret({
buffer: utf8Decode(str).buffer
});
}
/**
* Converts a base32 string to a Secret object.
* @param {string} str Base32 string.
* @returns {Secret} Secret object.
*/ static fromBase32(str) {
return new Secret({
buffer: base32Decode(str).buffer
});
}
/**
* Converts a hexadecimal string to a Secret object.
* @param {string} str Hexadecimal string.
* @returns {Secret} Secret object.
*/ static fromHex(str) {
return new Secret({
buffer: hexDecode(str).buffer
});
}
/**
* Secret key buffer.
* @deprecated For backward compatibility, the "bytes" property should be used instead.
* @type {ArrayBufferLike}
*/ get buffer() {
return this.bytes.buffer;
}
/**
* Latin-1 string representation of secret key.
* @type {string}
*/ get latin1() {
Object.defineProperty(this, "latin1", {
enumerable: true,
writable: false,
configurable: false,
value: latin1Encode(this.bytes)
});
return this.latin1;
}
/**
* UTF-8 string representation of secret key.
* @type {string}
*/ get utf8() {
Object.defineProperty(this, "utf8", {
enumerable: true,
writable: false,
configurable: false,
value: utf8Encode(this.bytes)
});
return this.utf8;
}
/**
* Base32 string representation of secret key.
* @type {string}
*/ get base32() {
Object.defineProperty(this, "base32", {
enumerable: true,
writable: false,
configurable: false,
value: base32Encode(this.bytes)
});
return this.base32;
}
/**
* Hexadecimal string representation of secret key.
* @type {string}
*/ get hex() {
Object.defineProperty(this, "hex", {
enumerable: true,
writable: false,
configurable: false,
value: hexEncode(this.bytes)
});
return this.hex;
}
/**
* Creates a secret key object.
* @param {Object} [config] Configuration options.
* @param {ArrayBufferLike} [config.buffer] Secret key buffer.
* @param {number} [config.size=20] Number of random bytes to generate, ignored if 'buffer' is provided.
*/ constructor({ buffer, size = 20 } = {}){
/**
* Secret key.
* @type {Uint8Array}
* @readonly
*/ this.bytes = typeof buffer === "undefined" ? randomBytes(size) : new Uint8Array(buffer);
// Prevent the "bytes" property from being modified.
Object.defineProperty(this, "bytes", {
enumerable: true,
writable: false,
configurable: false,
value: this.bytes
});
}
}
/**
* Returns true if a is equal to b, without leaking timing information that would allow an attacker to guess one of the values.
* @param {string} a String a.
* @param {string} b String b.
* @returns {boolean} Equality result.
*/ const timingSafeEqual = (a, b)=>{
{
if (a.length !== b.length) {
throw new TypeError("Input strings must have the same length");
}
let i = -1;
let out = 0;
while(++i < a.length){
out |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return out === 0;
}
};
/**
* HOTP: An HMAC-based One-time Password Algorithm.
* @see [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
*/ class HOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* counter: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
counter: 0,
window: 1
};
}
/**
* Generates an HOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac = hmacDigest }) {
const message = uintDecode(counter);
const digest = hmac(algorithm, secret.bytes, message);
if (!digest?.byteLength || digest.byteLength < 19) {
throw new TypeError("Return value must be at least 19 bytes");
}
const offset = digest[digest.byteLength - 1] & 15;
const otp = ((digest[offset] & 127) << 24 | (digest[offset + 1] & 255) << 16 | (digest[offset + 2] & 255) << 8 | digest[offset + 3] & 255) % 10 ** digits;
return otp.toString().padStart(digits, "0");
}
/**
* Generates an HOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.counter=this.counter++] Counter value.
* @returns {string} Token.
*/ generate({ counter = this.counter++ } = {}) {
return HOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
hmac: this.hmac
});
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, window = HOTP.defaults.window, hmac = hmacDigest }) {
// Return early if the token length does not match the digit number.
if (token.length !== digits) return null;
let delta = null;
const check = (/** @type {number} */ i)=>{
const generatedToken = HOTP.generate({
secret,
algorithm,
digits,
counter: i,
hmac
});
if (timingSafeEqual(token, generatedToken)) {
delta = i - counter;
}
};
check(counter);
for(let i = 1; i <= window && delta === null; ++i){
check(counter - i);
if (delta !== null) break;
check(counter + i);
if (delta !== null) break;
}
return delta;
}
/**
* Validates an HOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.counter=this.counter] Counter value.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, counter = this.counter, window }) {
return HOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
counter,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://hotp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `counter=${e(this.counter)}`;
}
/**
* Creates an HOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.counter=0] Initial counter value.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = HOTP.defaults.issuer, label = HOTP.defaults.label, issuerInLabel = HOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Initial counter value.
* @type {number}
*/ this.counter = counter;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
*/ class TOTP {
/**
* Default configuration.
* @type {{
* issuer: string,
* label: string,
* issuerInLabel: boolean,
* algorithm: string,
* digits: number,
* period: number
* window: number
* }}
*/ static get defaults() {
return {
issuer: "",
label: "OTPAuth",
issuerInLabel: true,
algorithm: "SHA1",
digits: 6,
period: 30,
window: 1
};
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ static counter({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return Math.floor(timestamp / 1000 / period);
}
/**
* Calculates the counter. i.e. the number of periods since timestamp 0.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} Counter.
*/ counter({ timestamp = Date.now() } = {}) {
return TOTP.counter({
period: this.period,
timestamp
});
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ static remaining({ period = TOTP.defaults.period, timestamp = Date.now() } = {}) {
return period * 1000 - timestamp % (period * 1000);
}
/**
* Calculates the remaining time in milliseconds until the next token is generated.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {number} counter.
*/ remaining({ timestamp = Date.now() } = {}) {
return TOTP.remaining({
period: this.period,
timestamp
});
}
/**
* Generates a TOTP token.
* @param {Object} config Configuration options.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {string} Token.
*/ static generate({ secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), hmac }) {
return HOTP.generate({
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
hmac
});
}
/**
* Generates a TOTP token.
* @param {Object} [config] Configuration options.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @returns {string} Token.
*/ generate({ timestamp = Date.now() } = {}) {
return TOTP.generate({
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
hmac: this.hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {Secret} config.secret Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ static validate({ token, secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now(), window, hmac }) {
return HOTP.validate({
token,
secret,
algorithm,
digits,
counter: TOTP.counter({
period,
timestamp
}),
window,
hmac
});
}
/**
* Validates a TOTP token.
* @param {Object} config Configuration options.
* @param {string} config.token Token value.
* @param {number} [config.timestamp=Date.now] Timestamp value in milliseconds.
* @param {number} [config.window=1] Window of counter values to test.
* @returns {number|null} Token delta or null if it is not found in the search window, in which case it should be considered invalid.
*/ validate({ token, timestamp, window }) {
return TOTP.validate({
token,
secret: this.secret,
algorithm: this.algorithm,
digits: this.digits,
period: this.period,
timestamp,
window,
hmac: this.hmac
});
}
/**
* Returns a Google Authenticator key URI.
* @returns {string} URI.
*/ toString() {
const e = encodeURIComponent;
return "otpauth://totp/" + `${this.issuer.length > 0 ? this.issuerInLabel ? `${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?issuer=${e(this.issuer)}&` : `${e(this.label)}?`}` + `secret=${e(this.secret.base32)}&` + `algorithm=${e(this.algorithm)}&` + `digits=${e(this.digits)}&` + `period=${e(this.period)}`;
}
/**
* Creates a TOTP object.
* @param {Object} [config] Configuration options.
* @param {string} [config.issuer=''] Account provider.
* @param {string} [config.label='OTPAuth'] Account label.
* @param {boolean} [config.issuerInLabel=true] Include issuer prefix in label.
* @param {Secret|string} [config.secret=Secret] Secret key.
* @param {string} [config.algorithm='SHA1'] HMAC hashing algorithm.
* @param {number} [config.digits=6] Token length.
* @param {number} [config.period=30] Token time-step duration.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
*/ constructor({ issuer = TOTP.defaults.issuer, label = TOTP.defaults.label, issuerInLabel = TOTP.defaults.issuerInLabel, secret = new Secret(), algorithm = TOTP.defaults.algorithm, digits = TOTP.defaults.digits, period = TOTP.defaults.period, hmac } = {}){
/**
* Account provider.
* @type {string}
*/ this.issuer = issuer;
/**
* Account label.
* @type {string}
*/ this.label = label;
/**
* Include issuer prefix in label.
* @type {boolean}
*/ this.issuerInLabel = issuerInLabel;
/**
* Secret key.
* @type {Secret}
*/ this.secret = typeof secret === "string" ? Secret.fromBase32(secret) : secret;
/**
* HMAC hashing algorithm.
* @type {string}
*/ this.algorithm = hmac ? algorithm : canonicalizeAlgorithm(algorithm);
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Token time-step duration.
* @type {number}
*/ this.period = period;
/**
* Custom HMAC function.
* @type {((algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array)|undefined}
*/ this.hmac = hmac;
}
}
/**
* Key URI regex (otpauth://TYPE/[ISSUER:]LABEL?PARAMETERS).
* @type {RegExp}
*/ const OTPURI_REGEX = /^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i;
/**
* RFC 4648 base32 alphabet with pad.
* @type {RegExp}
*/ const SECRET_REGEX = /^[2-7A-Z]+=*$/i;
/**
* Regex for supported algorithms in built-in HMAC function.
* @type {RegExp}
*/ const ALGORITHM_REGEX = /^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i;
/**
* Regex for custom algorithms in user-defined HMAC function.
* @type {RegExp}
*/ const ALGORITHM_CUSTOM_REGEX = /^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i;
/**
* Integer regex.
* @type {RegExp}
*/ const INTEGER_REGEX = /^[+-]?\d+$/;
/**
* Positive integer regex.
* @type {RegExp}
*/ const POSITIVE_INTEGER_REGEX = /^\+?[1-9]\d*$/;
/**
* HOTP/TOTP object/string conversion.
* @see [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
*/ class URI {
/**
* Parses a Google Authenticator key URI and returns an HOTP/TOTP object.
* @param {string} uri Google Authenticator Key URI.
* @param {Object} [config] Configuration options.
* @param {(algorithm: string, key: Uint8Array, message: Uint8Array) => Uint8Array} [config.hmac] Custom HMAC function.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/ static parse(uri, { hmac } = {}) {
let uriGroups;
try {
uriGroups = uri.match(OTPURI_REGEX);
// eslint-disable-next-line no-unused-vars
} catch (_) {
/* Handled below */ }
if (!Array.isArray(uriGroups)) {
throw new URIError("Invalid URI format");
}
// Extract URI groups.
const uriType = uriGroups[1].toLowerCase();
const uriLabel = uriGroups[2].split(/(?::|%3A) *(.+)/i, 2).map(decodeURIComponent);
/** @type {Object.<string, string>} */ const uriParams = uriGroups[3].split("&").reduce((acc, cur)=>{
const pairArr = cur.split(/=(.*)/, 2).map(decodeURIComponent);
const pairKey = pairArr[0].toLowerCase();
const pairVal = pairArr[1];
/** @type {Object.<string, string>} */ const pairAcc = acc;
pairAcc[pairKey] = pairVal;
return pairAcc;
}, {});
// 'OTP' will be instantiated with 'config' argument.
let OTP;
const config = {};
if (uriType === "hotp") {
OTP = HOTP;
// Counter: required
if (typeof uriParams.counter !== "undefined" && INTEGER_REGEX.test(uriParams.counter)) {
config.counter = parseInt(uriParams.counter, 10);
} else {
throw new TypeError("Missing or invalid 'counter' parameter");
}
} else if (uriType === "totp") {
OTP = TOTP;
// Period: optional
if (typeof uriParams.period !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.period)) {
config.period = parseInt(uriParams.period, 10);
} else {
throw new TypeError("Invalid 'period' parameter");
}
}
} else {
throw new TypeError("Unknown OTP type");
}
// Label: required
// Issuer: optional
if (typeof uriParams.issuer !== "undefined") {
config.issuer = uriParams.issuer;
}
if (uriLabel.length === 2) {
config.label = uriLabel[1];
if (typeof config.issuer === "undefined" || config.issuer === "") {
config.issuer = uriLabel[0];
} else if (uriLabel[0] === "") {
config.issuerInLabel = false;
}
} else {
config.label = uriLabel[0];
if (typeof config.issuer !== "undefined" && config.issuer !== "") {
config.issuerInLabel = false;
}
}
// Secret: required
if (typeof uriParams.secret !== "undefined" && SECRET_REGEX.test(uriParams.secret)) {
config.secret = uriParams.secret;
} else {
throw new TypeError("Missing or invalid 'secret' parameter");
}
// Algorithm: optional
if (typeof uriParams.algorithm !== "undefined") {
if ((hmac ? ALGORITHM_CUSTOM_REGEX : ALGORITHM_REGEX).test(uriParams.algorithm)) {
config.algorithm = uriParams.algorithm;
} else {
throw new TypeError("Invalid 'algorithm' parameter");
}
}
// Digits: optional
if (typeof uriParams.digits !== "undefined") {
if (POSITIVE_INTEGER_REGEX.test(uriParams.digits)) {
config.digits = parseInt(uriParams.digits, 10);
} else {
throw new TypeError("Invalid 'digits' parameter");
}
}
// HMAC: optional
if (typeof hmac !== "undefined") {
config.hmac = hmac;
}
return new OTP(config);
}
/**
* Converts an HOTP/TOTP object to a Google Authenticator key URI.
* @param {HOTP|TOTP} otp HOTP/TOTP object.
* @returns {string} Google Authenticator Key URI.
*/ static stringify(otp) {
if (otp instanceof HOTP || otp instanceof TOTP) {
return otp.toString();
}
throw new TypeError("Invalid 'HOTP/TOTP' object");
}
}
/**
* Library version.
* @type {string}
*/ const version = "9.5.0";
export { HOTP, Secret, TOTP, URI, version };

View File

@@ -0,0 +1,10 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
//! noble-hashes 2.0.1 | (c) Paul Miller | MIT | https://github.com/paulmillr/noble-hashes
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
import{hmac as e}from"@noble/hashes/hmac.js";import{sha1 as t}from"@noble/hashes/legacy.js";import{sha512 as r,sha384 as i,sha256 as s,sha224 as n}from"@noble/hashes/sha2.js";import{sha3_512 as o,sha3_384 as a,sha3_256 as l,sha3_224 as u}from"@noble/hashes/sha3.js";const h=(()=>{if("object"==typeof globalThis)return globalThis;Object.defineProperty(Object.prototype,"__GLOBALTHIS__",{get(){return this},configurable:!0});try{if("undefined"!=typeof __GLOBALTHIS__)return __GLOBALTHIS__}finally{delete Object.prototype.__GLOBALTHIS__}return"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0})(),c={SHA1:t,SHA224:n,SHA256:s,SHA384:i,SHA512:r,"SHA3-224":u,"SHA3-256":l,"SHA3-384":a,"SHA3-512":o},d=e=>{switch(!0){case/^(?:SHA-?1|SSL3-SHA1)$/i.test(e):return"SHA1";case/^SHA(?:2?-)?224$/i.test(e):return"SHA224";case/^SHA(?:2?-)?256$/i.test(e):return"SHA256";case/^SHA(?:2?-)?384$/i.test(e):return"SHA384";case/^SHA(?:2?-)?512$/i.test(e):return"SHA512";case/^SHA3-224$/i.test(e):return"SHA3-224";case/^SHA3-256$/i.test(e):return"SHA3-256";case/^SHA3-384$/i.test(e):return"SHA3-384";case/^SHA3-512$/i.test(e):return"SHA3-512";default:throw new TypeError(`Unknown hash algorithm: ${e}`)}},g=(t,r,i)=>{if(e){const s=c[t]??c[d(t)];return e(s,r,i)}throw new Error("Missing HMAC function")},f="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",m=e=>{let t=(e=e.replace(/ /g,"")).length;for(;"="===e[t-1];)--t;e=(t<e.length?e.substring(0,t):e).toUpperCase();const r=new ArrayBuffer(5*e.length/8|0),i=new Uint8Array(r);let s=0,n=0,o=0;for(let t=0;t<e.length;t++){const r=f.indexOf(e[t]);if(-1===r)throw new TypeError(`Invalid character found: ${e[t]}`);n=n<<5|r,s+=5,s>=8&&(s-=8,i[o++]=n>>>s)}return i},p=e=>{let t=0,r=0,i="";for(let s=0;s<e.length;s++)for(r=r<<8|e[s],t+=8;t>=5;)i+=f[r>>>t-5&31],t-=5;return t>0&&(i+=f[r<<5-t&31]),i},b=e=>{e=e.replace(/ /g,"");const t=new ArrayBuffer(e.length/2),r=new Uint8Array(t);for(let t=0;t<e.length;t+=2)r[t/2]=parseInt(e.substring(t,t+2),16);return r},w=e=>{let t=""
;for(let r=0;r<e.length;r++){const i=e[r].toString(16);1===i.length&&(t+="0"),t+=i}return t.toUpperCase()},A=e=>{const t=new ArrayBuffer(e.length),r=new Uint8Array(t);for(let t=0;t<e.length;t++)r[t]=255&e.charCodeAt(t);return r},y=e=>{let t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t},S=h.TextEncoder?new h.TextEncoder:null,H=h.TextDecoder?new h.TextDecoder:null,I=e=>{if(!S)throw new Error("Encoding API not available");return S.encode(e)},$=e=>{if(!H)throw new Error("Encoding API not available");return H.decode(e)};class v{static fromLatin1(e){return new v({buffer:A(e).buffer})}static fromUTF8(e){return new v({buffer:I(e).buffer})}static fromBase32(e){return new v({buffer:m(e).buffer})}static fromHex(e){return new v({buffer:b(e).buffer})}get buffer(){return this.bytes.buffer}get latin1(){return Object.defineProperty(this,"latin1",{enumerable:!0,writable:!1,configurable:!1,value:y(this.bytes)}),this.latin1}get utf8(){return Object.defineProperty(this,"utf8",{enumerable:!0,writable:!1,configurable:!1,value:$(this.bytes)}),this.utf8}get base32(){return Object.defineProperty(this,"base32",{enumerable:!0,writable:!1,configurable:!1,value:p(this.bytes)}),this.base32}get hex(){return Object.defineProperty(this,"hex",{enumerable:!0,writable:!1,configurable:!1,value:w(this.bytes)}),this.hex}constructor({buffer:e,size:t=20}={}){this.bytes=void 0===e?(e=>{if(h.crypto?.getRandomValues)return h.crypto.getRandomValues(new Uint8Array(e));throw new Error("Cryptography API not available")})(t):new Uint8Array(e),Object.defineProperty(this,"bytes",{enumerable:!0,writable:!1,configurable:!1,value:this.bytes})}}class T{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,counter:0,window:1}}static generate({secret:e,algorithm:t=T.defaults.algorithm,digits:r=T.defaults.digits,counter:i=T.defaults.counter,hmac:s=g}){const n=(e=>{const t=new ArrayBuffer(8),r=new Uint8Array(t);let i=e;for(let e=7;e>=0&&0!==i;e--)r[e]=255&i,i-=r[e],i/=256;return r})(i),o=s(t,e.bytes,n)
;if(!o?.byteLength||o.byteLength<19)throw new TypeError("Return value must be at least 19 bytes");const a=15&o[o.byteLength-1];return(((127&o[a])<<24|(255&o[a+1])<<16|(255&o[a+2])<<8|255&o[a+3])%10**r).toString().padStart(r,"0")}generate({counter:e=this.counter++}={}){return T.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:e,hmac:this.hmac})}static validate({token:e,secret:t,algorithm:r,digits:i=T.defaults.digits,counter:s=T.defaults.counter,window:n=T.defaults.window,hmac:o=g}){if(e.length!==i)return null;let a=null;const l=n=>{const l=T.generate({secret:t,algorithm:r,digits:i,counter:n,hmac:o});((e,t)=>{{if(e.length!==t.length)throw new TypeError("Input strings must have the same length");let r=-1,i=0;for(;++r<e.length;)i|=e.charCodeAt(r)^t.charCodeAt(r);return 0===i}})(e,l)&&(a=n-s)};l(s);for(let e=1;e<=n&&null===a&&(l(s-e),null===a)&&(l(s+e),null===a);++e);return a}validate({token:e,counter:t=this.counter,window:r}){return T.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://hotp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`counter=${e(this.counter)}`}constructor({issuer:e=T.defaults.issuer,label:t=T.defaults.label,issuerInLabel:r=T.defaults.issuerInLabel,secret:i=new v,algorithm:s=T.defaults.algorithm,digits:n=T.defaults.digits,counter:o=T.defaults.counter,hmac:a}={}){this.issuer=e,this.label=t,this.issuerInLabel=r,this.secret="string"==typeof i?v.fromBase32(i):i,this.algorithm=a?s:d(s),this.digits=n,this.counter=o,this.hmac=a}}class L{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,period:30,window:1}}static counter({period:e=L.defaults.period,timestamp:t=Date.now()}={}){
return Math.floor(t/1e3/e)}counter({timestamp:e=Date.now()}={}){return L.counter({period:this.period,timestamp:e})}static remaining({period:e=L.defaults.period,timestamp:t=Date.now()}={}){return 1e3*e-t%(1e3*e)}remaining({timestamp:e=Date.now()}={}){return L.remaining({period:this.period,timestamp:e})}static generate({secret:e,algorithm:t,digits:r,period:i=L.defaults.period,timestamp:s=Date.now(),hmac:n}){return T.generate({secret:e,algorithm:t,digits:r,counter:L.counter({period:i,timestamp:s}),hmac:n})}generate({timestamp:e=Date.now()}={}){return L.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:e,hmac:this.hmac})}static validate({token:e,secret:t,algorithm:r,digits:i,period:s=L.defaults.period,timestamp:n=Date.now(),window:o,hmac:a}){return T.validate({token:e,secret:t,algorithm:r,digits:i,counter:L.counter({period:s,timestamp:n}),window:o,hmac:a})}validate({token:e,timestamp:t,window:r}){return L.validate({token:e,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:t,window:r,hmac:this.hmac})}toString(){const e=encodeURIComponent;return"otpauth://totp/"+(this.issuer.length>0?this.issuerInLabel?`${e(this.issuer)}:${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?issuer=${e(this.issuer)}&`:`${e(this.label)}?`)+`secret=${e(this.secret.base32)}&`+`algorithm=${e(this.algorithm)}&`+`digits=${e(this.digits)}&`+`period=${e(this.period)}`}constructor({issuer:e=L.defaults.issuer,label:t=L.defaults.label,issuerInLabel:r=L.defaults.issuerInLabel,secret:i=new v,algorithm:s=L.defaults.algorithm,digits:n=L.defaults.digits,period:o=L.defaults.period,hmac:a}={}){this.issuer=e,this.label=t,this.issuerInLabel=r,this.secret="string"==typeof i?v.fromBase32(i):i,this.algorithm=a?s:d(s),this.digits=n,this.period=o,this.hmac=a}}
const E=/^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i,O=/^[2-7A-Z]+=*$/i,_=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,U=/^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i,C=/^[+-]?\d+$/,P=/^\+?[1-9]\d*$/;class j{static parse(e,{hmac:t}={}){let r;try{r=e.match(E)}catch(e){}if(!Array.isArray(r))throw new URIError("Invalid URI format");const i=r[1].toLowerCase(),s=r[2].split(/(?::|%3A) *(.+)/i,2).map(decodeURIComponent),n=r[3].split("&").reduce((e,t)=>{const r=t.split(/=(.*)/,2).map(decodeURIComponent),i=r[0].toLowerCase(),s=r[1],n=e;return n[i]=s,n},{});let o;const a={};if("hotp"===i){if(o=T,void 0===n.counter||!C.test(n.counter))throw new TypeError("Missing or invalid 'counter' parameter");a.counter=parseInt(n.counter,10)}else{if("totp"!==i)throw new TypeError("Unknown OTP type");if(o=L,void 0!==n.period){if(!P.test(n.period))throw new TypeError("Invalid 'period' parameter");a.period=parseInt(n.period,10)}}if(void 0!==n.issuer&&(a.issuer=n.issuer),2===s.length?(a.label=s[1],void 0===a.issuer||""===a.issuer?a.issuer=s[0]:""===s[0]&&(a.issuerInLabel=!1)):(a.label=s[0],void 0!==a.issuer&&""!==a.issuer&&(a.issuerInLabel=!1)),void 0===n.secret||!O.test(n.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(a.secret=n.secret,void 0!==n.algorithm){if(!(t?U:_).test(n.algorithm))throw new TypeError("Invalid 'algorithm' parameter");a.algorithm=n.algorithm}if(void 0!==n.digits){if(!P.test(n.digits))throw new TypeError("Invalid 'digits' parameter");a.digits=parseInt(n.digits,10)}return void 0!==t&&(a.hmac=t),new o(a)}static stringify(e){if(e instanceof T||e instanceof L)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}const B="9.5.0";export{T as HOTP,v as Secret,L as TOTP,j as URI,B as version};
//# sourceMappingURL=otpauth.slim.esm.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
//! otpauth 9.5.0 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
//! noble-hashes 2.0.1 | (c) Paul Miller | MIT | https://github.com/paulmillr/noble-hashes
/// <reference types="./otpauth.d.ts" />
// @ts-nocheck
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).OTPAuth={})}(this,function(t){"use strict";function e(t,e=""){if(!Number.isSafeInteger(t)||t<0)throw new Error(`${e&&`"${e}" `}expected integer >= 0, got ${t}`)}function s(t,e,s=""){const i=(r=t)instanceof Uint8Array||ArrayBuffer.isView(r)&&"Uint8Array"===r.constructor.name;var r;const n=t?.length,o=void 0!==e;if(!i||o&&n!==e)throw new Error((s&&`"${s}" `)+"expected Uint8Array"+(o?` of length ${e}`:"")+", got "+(i?`length=${n}`:"type="+typeof t));return t}function i(t,e=!0){if(t.destroyed)throw new Error("Hash instance has been destroyed");if(e&&t.finished)throw new Error("Hash#digest() has already been called")}function r(t,e){s(t,void 0,"digestInto() output");const i=e.outputLen;if(t.length<i)throw new Error('"digestInto() output" expected to be of length >='+i)}function n(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function o(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function h(t,e){return t<<32-e|t>>>e}function a(t,e){return t<<e|t>>>32-e>>>0}function c(t){return t<<24&4278190080|t<<8&16711680|t>>>8&65280|t>>>24&255}const l=(()=>68===new Uint8Array(new Uint32Array([287454020]).buffer)[0])()?t=>t:function(t){for(let e=0;e<t.length;e++)t[e]=c(t[e]);return t};function u(t,e={}){const s=(e,s)=>t(s).update(e).digest(),i=t(void 0);return s.outputLen=i.outputLen,s.blockLen=i.blockLen,s.create=e=>t(e),Object.assign(s,e),Object.freeze(s)}const f=t=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,t])});class d{update(t){return i(this),this.iHash.update(t),this}digestInto(t){i(this),s(t,this.outputLen,"output"),this.finished=!0,this.iHash.digestInto(t),this.oHash.update(t),this.oHash.digestInto(t),this.destroy()}digest(){const t=new Uint8Array(this.oHash.outputLen);return this.digestInto(t),t}_cloneInto(t){t||(t=Object.create(Object.getPrototypeOf(this),{}))
;const{oHash:e,iHash:s,finished:i,destroyed:r,blockLen:n,outputLen:o}=this;return t.finished=i,t.destroyed=r,t.blockLen=n,t.outputLen=o,t.oHash=e._cloneInto(t.oHash),t.iHash=s._cloneInto(t.iHash),t}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}constructor(t,i){if(this.finished=!1,this.destroyed=!1,function(t){if("function"!=typeof t||"function"!=typeof t.create)throw new Error("Hash must wrapped by utils.createHasher");e(t.outputLen),e(t.blockLen)}(t),s(i,void 0,"key"),this.iHash=t.create(),"function"!=typeof this.iHash.update)throw new Error("Expected instance of class which extends utils.Hash");this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;const r=this.blockLen,o=new Uint8Array(r);o.set(i.length>r?t.create().update(i).digest():i);for(let t=0;t<o.length;t++)o[t]^=54;this.iHash.update(o),this.oHash=t.create();for(let t=0;t<o.length;t++)o[t]^=106;this.oHash.update(o),n(o)}}const b=(t,e,s)=>new d(t,e).update(s).digest();function g(t,e,s){return t&e^~t&s}function p(t,e,s){return t&e^t&s^e&s}b.create=(t,e)=>new d(t,e);class w{update(t){i(this),s(t);const{view:e,buffer:r,blockLen:n}=this,h=t.length;for(let s=0;s<h;){const i=Math.min(n-this.pos,h-s);if(i===n){const e=o(t);for(;n<=h-s;s+=n)this.process(e,s);continue}r.set(t.subarray(s,s+i),this.pos),this.pos+=i,s+=i,this.pos===n&&(this.process(e,0),this.pos=0)}return this.length+=t.length,this.roundClean(),this}digestInto(t){i(this),r(t,this),this.finished=!0;const{buffer:e,view:s,blockLen:h,isLE:a}=this;let{pos:c}=this;e[c++]=128,n(this.buffer.subarray(c)),this.padOffset>h-c&&(this.process(s,0),c=0);for(let t=c;t<h;t++)e[t]=0;s.setBigUint64(h-8,BigInt(8*this.length),a),this.process(s,0);const l=o(t),u=this.outputLen;if(u%4)throw new Error("_sha2: outputLen must be aligned to 32bit");const f=u/4,d=this.get();if(f>d.length)throw new Error("_sha2: outputLen bigger than state");for(let t=0;t<f;t++)l.setUint32(4*t,d[t],a)}digest(){const{buffer:t,outputLen:e}=this;this.digestInto(t)
;const s=t.slice(0,e);return this.destroy(),s}_cloneInto(t){t||(t=new this.constructor),t.set(...this.get());const{blockLen:e,buffer:s,length:i,finished:r,destroyed:n,pos:o}=this;return t.destroyed=n,t.finished=r,t.length=i,t.pos=o,i%e&&t.buffer.set(s),t}clone(){return this._cloneInto()}constructor(t,e,s,i){this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=t,this.outputLen=e,this.padOffset=s,this.isLE=i,this.buffer=new Uint8Array(t),this.view=o(this.buffer)}}const m=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),y=Uint32Array.from([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428]),A=Uint32Array.from([3418070365,3238371032,1654270250,914150663,2438529370,812702999,355462360,4144912697,1731405415,4290775857,2394180231,1750603025,3675008525,1694076839,1203062813,3204075428]),x=Uint32Array.from([1779033703,4089235720,3144134277,2227873595,1013904242,4271175723,2773480762,1595750129,1359893119,2917565137,2600822924,725511199,528734635,4215389547,1541459225,327033209]),H=Uint32Array.from([1732584193,4023233417,2562383102,271733878,3285377520]),I=new Uint32Array(80);class L extends w{get(){const{A:t,B:e,C:s,D:i,E:r}=this;return[t,e,s,i,r]}set(t,e,s,i,r){this.A=0|t,this.B=0|e,this.C=0|s,this.D=0|i,this.E=0|r}process(t,e){for(let s=0;s<16;s++,e+=4)I[s]=t.getUint32(e,!1);for(let t=16;t<80;t++)I[t]=a(I[t-3]^I[t-8]^I[t-14]^I[t-16],1);let{A:s,B:i,C:r,D:n,E:o}=this;for(let t=0;t<80;t++){let e,h;t<20?(e=g(i,r,n),h=1518500249):t<40?(e=i^r^n,h=1859775393):t<60?(e=p(i,r,n),h=2400959708):(e=i^r^n,h=3395469782);const c=a(s,5)+e+o+h+I[t]|0;o=n,n=r,r=a(i,30),i=s,s=c}s=s+this.A|0,i=i+this.B|0,r=r+this.C|0,n=n+this.D|0,o=o+this.E|0,this.set(s,i,r,n,o)}roundClean(){n(I)}destroy(){this.set(0,0,0,0,0),n(this.buffer)}constructor(){super(64,20,8,!1),this.A=0|H[0],this.B=0|H[1],this.C=0|H[2],this.D=0|H[3],this.E=0|H[4]}}const E=u(()=>new L),U=BigInt(2**32-1),B=BigInt(32);function S(t,e=!1){return e?{h:Number(t&U),
l:Number(t>>B&U)}:{h:0|Number(t>>B&U),l:0|Number(t&U)}}function v(t,e=!1){const s=t.length;let i=new Uint32Array(s),r=new Uint32Array(s);for(let n=0;n<s;n++){const{h:s,l:o}=S(t[n],e);[i[n],r[n]]=[s,o]}return[i,r]}const O=(t,e,s)=>t>>>s,C=(t,e,s)=>t<<32-s|e>>>s,$=(t,e,s)=>t>>>s|e<<32-s,k=(t,e,s)=>t<<32-s|e>>>s,T=(t,e,s)=>t<<64-s|e>>>s-32,D=(t,e,s)=>t>>>s-32|e<<64-s;function _(t,e,s,i){const r=(e>>>0)+(i>>>0);return{h:t+s+(r/2**32|0)|0,l:0|r}}const F=(t,e,s)=>(t>>>0)+(e>>>0)+(s>>>0),G=(t,e,s,i)=>e+s+i+(t/2**32|0)|0,P=(t,e,s,i)=>(t>>>0)+(e>>>0)+(s>>>0)+(i>>>0),j=(t,e,s,i,r)=>e+s+i+r+(t/2**32|0)|0,R=(t,e,s,i,r)=>(t>>>0)+(e>>>0)+(s>>>0)+(i>>>0)+(r>>>0),M=(t,e,s,i,r,n)=>e+s+i+r+n+(t/2**32|0)|0,X=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),N=new Uint32Array(64);class Z extends w{get(){const{A:t,B:e,C:s,D:i,E:r,F:n,G:o,H:h}=this;return[t,e,s,i,r,n,o,h]}set(t,e,s,i,r,n,o,h){this.A=0|t,this.B=0|e,this.C=0|s,this.D=0|i,this.E=0|r,this.F=0|n,this.G=0|o,this.H=0|h}process(t,e){for(let s=0;s<16;s++,e+=4)N[s]=t.getUint32(e,!1);for(let t=16;t<64;t++){const e=N[t-15],s=N[t-2],i=h(e,7)^h(e,18)^e>>>3,r=h(s,17)^h(s,19)^s>>>10;N[t]=r+N[t-7]+i+N[t-16]|0}let{A:s,B:i,C:r,D:n,E:o,F:a,G:c,H:l}=this;for(let t=0;t<64;t++){const e=l+(h(o,6)^h(o,11)^h(o,25))+g(o,a,c)+X[t]+N[t]|0,u=(h(s,2)^h(s,13)^h(s,22))+p(s,i,r)|0;l=c,c=a,a=o,o=n+e|0,n=r,r=i,i=s,s=e+u|0}s=s+this.A|0,i=i+this.B|0,
r=r+this.C|0,n=n+this.D|0,o=o+this.E|0,a=a+this.F|0,c=c+this.G|0,l=l+this.H|0,this.set(s,i,r,n,o,a,c,l)}roundClean(){n(N)}destroy(){this.set(0,0,0,0,0,0,0,0),n(this.buffer)}constructor(t){super(64,t,8,!1)}}class V extends Z{constructor(){super(32),this.A=0|m[0],this.B=0|m[1],this.C=0|m[2],this.D=0|m[3],this.E=0|m[4],this.F=0|m[5],this.G=0|m[6],this.H=0|m[7]}}class z extends Z{constructor(){super(28),this.A=0|y[0],this.B=0|y[1],this.C=0|y[2],this.D=0|y[3],this.E=0|y[4],this.F=0|y[5],this.G=0|y[6],this.H=0|y[7]}}
const J=(()=>v(["0x428a2f98d728ae22","0x7137449123ef65cd","0xb5c0fbcfec4d3b2f","0xe9b5dba58189dbbc","0x3956c25bf348b538","0x59f111f1b605d019","0x923f82a4af194f9b","0xab1c5ed5da6d8118","0xd807aa98a3030242","0x12835b0145706fbe","0x243185be4ee4b28c","0x550c7dc3d5ffb4e2","0x72be5d74f27b896f","0x80deb1fe3b1696b1","0x9bdc06a725c71235","0xc19bf174cf692694","0xe49b69c19ef14ad2","0xefbe4786384f25e3","0x0fc19dc68b8cd5b5","0x240ca1cc77ac9c65","0x2de92c6f592b0275","0x4a7484aa6ea6e483","0x5cb0a9dcbd41fbd4","0x76f988da831153b5","0x983e5152ee66dfab","0xa831c66d2db43210","0xb00327c898fb213f","0xbf597fc7beef0ee4","0xc6e00bf33da88fc2","0xd5a79147930aa725","0x06ca6351e003826f","0x142929670a0e6e70","0x27b70a8546d22ffc","0x2e1b21385c26c926","0x4d2c6dfc5ac42aed","0x53380d139d95b3df","0x650a73548baf63de","0x766a0abb3c77b2a8","0x81c2c92e47edaee6","0x92722c851482353b","0xa2bfe8a14cf10364","0xa81a664bbc423001","0xc24b8b70d0f89791","0xc76c51a30654be30","0xd192e819d6ef5218","0xd69906245565a910","0xf40e35855771202a","0x106aa07032bbd1b8","0x19a4c116b8d2d0c8","0x1e376c085141ab53","0x2748774cdf8eeb99","0x34b0bcb5e19b48a8","0x391c0cb3c5c95a63","0x4ed8aa4ae3418acb","0x5b9cca4f7763e373","0x682e6ff3d6b2b8a3","0x748f82ee5defb2fc","0x78a5636f43172f60","0x84c87814a1f0ab72","0x8cc702081a6439ec","0x90befffa23631e28","0xa4506cebde82bde9","0xbef9a3f7b2c67915","0xc67178f2e372532b","0xca273eceea26619c","0xd186b8c721c0c207","0xeada7dd6cde0eb1e","0xf57d4f7fee6ed178","0x06f067aa72176fba","0x0a637dc5a2c898a6","0x113f9804bef90dae","0x1b710b35131c471b","0x28db77f523047d84","0x32caab7b40c72493","0x3c9ebe0a15c9bebc","0x431d67c49c100d4c","0x4cc5d4becb3e42b6","0x597f299cfc657e2a","0x5fcb6fab3ad6faec","0x6c44198c4a475817"].map(t=>BigInt(t))))(),K=(()=>J[0])(),Q=(()=>J[1])(),W=new Uint32Array(80),Y=new Uint32Array(80);class q extends w{get(){const{Ah:t,Al:e,Bh:s,Bl:i,Ch:r,Cl:n,Dh:o,Dl:h,Eh:a,El:c,Fh:l,Fl:u,Gh:f,Gl:d,Hh:b,Hl:g}=this;return[t,e,s,i,r,n,o,h,a,c,l,u,f,d,b,g]}set(t,e,s,i,r,n,o,h,a,c,l,u,f,d,b,g){this.Ah=0|t,this.Al=0|e,this.Bh=0|s,this.Bl=0|i,this.Ch=0|r,
this.Cl=0|n,this.Dh=0|o,this.Dl=0|h,this.Eh=0|a,this.El=0|c,this.Fh=0|l,this.Fl=0|u,this.Gh=0|f,this.Gl=0|d,this.Hh=0|b,this.Hl=0|g}process(t,e){for(let s=0;s<16;s++,e+=4)W[s]=t.getUint32(e),Y[s]=t.getUint32(e+=4);for(let t=16;t<80;t++){const e=0|W[t-15],s=0|Y[t-15],i=$(e,s,1)^$(e,s,8)^O(e,0,7),r=k(e,s,1)^k(e,s,8)^C(e,s,7),n=0|W[t-2],o=0|Y[t-2],h=$(n,o,19)^T(n,o,61)^O(n,0,6),a=k(n,o,19)^D(n,o,61)^C(n,o,6),c=P(r,a,Y[t-7],Y[t-16]),l=j(c,i,h,W[t-7],W[t-16]);W[t]=0|l,Y[t]=0|c}let{Ah:s,Al:i,Bh:r,Bl:n,Ch:o,Cl:h,Dh:a,Dl:c,Eh:l,El:u,Fh:f,Fl:d,Gh:b,Gl:g,Hh:p,Hl:w}=this;for(let t=0;t<80;t++){const e=$(l,u,14)^$(l,u,18)^T(l,u,41),m=k(l,u,14)^k(l,u,18)^D(l,u,41),y=l&f^~l&b,A=R(w,m,u&d^~u&g,Q[t],Y[t]),x=M(A,p,e,y,K[t],W[t]),H=0|A,I=$(s,i,28)^T(s,i,34)^T(s,i,39),L=k(s,i,28)^D(s,i,34)^D(s,i,39),E=s&r^s&o^r&o,U=i&n^i&h^n&h;p=0|b,w=0|g,b=0|f,g=0|d,f=0|l,d=0|u,({h:l,l:u}=_(0|a,0|c,0|x,0|H)),a=0|o,c=0|h,o=0|r,h=0|n,r=0|s,n=0|i;const B=F(H,L,U);s=G(B,x,I,E),i=0|B}({h:s,l:i}=_(0|this.Ah,0|this.Al,0|s,0|i)),({h:r,l:n}=_(0|this.Bh,0|this.Bl,0|r,0|n)),({h:o,l:h}=_(0|this.Ch,0|this.Cl,0|o,0|h)),({h:a,l:c}=_(0|this.Dh,0|this.Dl,0|a,0|c)),({h:l,l:u}=_(0|this.Eh,0|this.El,0|l,0|u)),({h:f,l:d}=_(0|this.Fh,0|this.Fl,0|f,0|d)),({h:b,l:g}=_(0|this.Gh,0|this.Gl,0|b,0|g)),({h:p,l:w}=_(0|this.Hh,0|this.Hl,0|p,0|w)),this.set(s,i,r,n,o,h,a,c,l,u,f,d,b,g,p,w)}roundClean(){n(W,Y)}destroy(){n(this.buffer),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}constructor(t){super(128,t,16,!1)}}class tt extends q{constructor(){super(64),this.Ah=0|x[0],this.Al=0|x[1],this.Bh=0|x[2],this.Bl=0|x[3],this.Ch=0|x[4],this.Cl=0|x[5],this.Dh=0|x[6],this.Dl=0|x[7],this.Eh=0|x[8],this.El=0|x[9],this.Fh=0|x[10],this.Fl=0|x[11],this.Gh=0|x[12],this.Gl=0|x[13],this.Hh=0|x[14],this.Hl=0|x[15]}}class et extends q{constructor(){super(48),this.Ah=0|A[0],this.Al=0|A[1],this.Bh=0|A[2],this.Bl=0|A[3],this.Ch=0|A[4],this.Cl=0|A[5],this.Dh=0|A[6],this.Dl=0|A[7],this.Eh=0|A[8],this.El=0|A[9],this.Fh=0|A[10],this.Fl=0|A[11],this.Gh=0|A[12],this.Gl=0|A[13],this.Hh=0|A[14],this.Hl=0|A[15]}}
const st=u(()=>new V,f(1)),it=u(()=>new z,f(4)),rt=u(()=>new tt,f(3)),nt=u(()=>new et,f(2)),ot=BigInt(0),ht=BigInt(1),at=BigInt(2),ct=BigInt(7),lt=BigInt(256),ut=BigInt(113),ft=[],dt=[],bt=[];for(let t=0,e=ht,s=1,i=0;t<24;t++){[s,i]=[i,(2*s+3*i)%5],ft.push(2*(5*i+s)),dt.push((t+1)*(t+2)/2%64);let r=ot;for(let t=0;t<7;t++)e=(e<<ht^(e>>ct)*ut)%lt,e&at&&(r^=ht<<(ht<<BigInt(t))-ht);bt.push(r)}const gt=v(bt,!0),pt=gt[0],wt=gt[1],mt=(t,e,s)=>s>32?((t,e,s)=>e<<s-32|t>>>64-s)(t,e,s):((t,e,s)=>t<<s|e>>>32-s)(t,e,s),yt=(t,e,s)=>s>32?((t,e,s)=>t<<s-32|e>>>64-s)(t,e,s):((t,e,s)=>e<<s|t>>>32-s)(t,e,s);class At{clone(){return this._cloneInto()}keccak(){l(this.state32),function(t,e=24){const s=new Uint32Array(10);for(let i=24-e;i<24;i++){for(let e=0;e<10;e++)s[e]=t[e]^t[e+10]^t[e+20]^t[e+30]^t[e+40];for(let e=0;e<10;e+=2){const i=(e+8)%10,r=(e+2)%10,n=s[r],o=s[r+1],h=mt(n,o,1)^s[i],a=yt(n,o,1)^s[i+1];for(let s=0;s<50;s+=10)t[e+s]^=h,t[e+s+1]^=a}let e=t[2],r=t[3];for(let s=0;s<24;s++){const i=dt[s],n=mt(e,r,i),o=yt(e,r,i),h=ft[s];e=t[h],r=t[h+1],t[h]=n,t[h+1]=o}for(let e=0;e<50;e+=10){for(let i=0;i<10;i++)s[i]=t[e+i];for(let i=0;i<10;i++)t[e+i]^=~s[(i+2)%10]&s[(i+4)%10]}t[0]^=pt[i],t[1]^=wt[i]}n(s)}(this.state32,this.rounds),l(this.state32),this.posOut=0,this.pos=0}update(t){i(this),s(t);const{blockLen:e,state:r}=this,n=t.length;for(let s=0;s<n;){const i=Math.min(e-this.pos,n-s);for(let e=0;e<i;e++)r[this.pos++]^=t[s++];this.pos===e&&this.keccak()}return this}finish(){if(this.finished)return;this.finished=!0;const{state:t,suffix:e,pos:s,blockLen:i}=this;t[s]^=e,128&e&&s===i-1&&this.keccak(),t[i-1]^=128,this.keccak()}writeInto(t){i(this,!1),s(t),this.finish();const e=this.state,{blockLen:r}=this;for(let s=0,i=t.length;s<i;){this.posOut>=r&&this.keccak();const n=Math.min(r-this.posOut,i-s);t.set(e.subarray(this.posOut,this.posOut+n),s),this.posOut+=n,s+=n}return t}xofInto(t){if(!this.enableXOF)throw new Error("XOF is not possible for this instance");return this.writeInto(t)}xof(t){return e(t),this.xofInto(new Uint8Array(t))}
digestInto(t){if(r(t,this),this.finished)throw new Error("digest() was already called");return this.writeInto(t),this.destroy(),t}digest(){return this.digestInto(new Uint8Array(this.outputLen))}destroy(){this.destroyed=!0,n(this.state)}_cloneInto(t){const{blockLen:e,suffix:s,outputLen:i,rounds:r,enableXOF:n}=this;return t||(t=new At(e,s,i,n,r)),t.state32.set(this.state32),t.pos=this.pos,t.posOut=this.posOut,t.finished=this.finished,t.rounds=r,t.suffix=s,t.outputLen=i,t.enableXOF=n,t.destroyed=this.destroyed,t}constructor(t,s,i,r=!1,n=24){if(this.pos=0,this.posOut=0,this.finished=!1,this.destroyed=!1,this.enableXOF=!1,this.blockLen=t,this.suffix=s,this.outputLen=i,this.enableXOF=r,this.rounds=n,e(i,"outputLen"),!(0<t&&t<200))throw new Error("only keccak-f1600 function is supported");var o;this.state=new Uint8Array(200),this.state32=(o=this.state,new Uint32Array(o.buffer,o.byteOffset,Math.floor(o.byteLength/4)))}}const xt=(t,e,s,i={})=>u(()=>new At(e,t,s),i),Ht=xt(6,144,28,f(7)),It=xt(6,136,32,f(8)),Lt=xt(6,104,48,f(9)),Et=xt(6,72,64,f(10)),Ut=(()=>{if("object"==typeof globalThis)return globalThis;Object.defineProperty(Object.prototype,"__GLOBALTHIS__",{get(){return this},configurable:!0});try{if("undefined"!=typeof __GLOBALTHIS__)return __GLOBALTHIS__}finally{delete Object.prototype.__GLOBALTHIS__}return"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0})(),Bt={SHA1:E,SHA224:it,SHA256:st,SHA384:nt,SHA512:rt,"SHA3-224":Ht,"SHA3-256":It,"SHA3-384":Lt,"SHA3-512":Et},St=t=>{switch(!0){case/^(?:SHA-?1|SSL3-SHA1)$/i.test(t):return"SHA1";case/^SHA(?:2?-)?224$/i.test(t):return"SHA224";case/^SHA(?:2?-)?256$/i.test(t):return"SHA256";case/^SHA(?:2?-)?384$/i.test(t):return"SHA384";case/^SHA(?:2?-)?512$/i.test(t):return"SHA512";case/^SHA3-224$/i.test(t):return"SHA3-224";case/^SHA3-256$/i.test(t):return"SHA3-256";case/^SHA3-384$/i.test(t):return"SHA3-384";case/^SHA3-512$/i.test(t):return"SHA3-512";default:throw new TypeError(`Unknown hash algorithm: ${t}`)}},vt=(t,e,s)=>{
if(b){const i=Bt[t]??Bt[St(t)];return b(i,e,s)}throw new Error("Missing HMAC function")},Ot="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",Ct=t=>{let e=(t=t.replace(/ /g,"")).length;for(;"="===t[e-1];)--e;t=(e<t.length?t.substring(0,e):t).toUpperCase();const s=new ArrayBuffer(5*t.length/8|0),i=new Uint8Array(s);let r=0,n=0,o=0;for(let e=0;e<t.length;e++){const s=Ot.indexOf(t[e]);if(-1===s)throw new TypeError(`Invalid character found: ${t[e]}`);n=n<<5|s,r+=5,r>=8&&(r-=8,i[o++]=n>>>r)}return i},$t=t=>{let e=0,s=0,i="";for(let r=0;r<t.length;r++)for(s=s<<8|t[r],e+=8;e>=5;)i+=Ot[s>>>e-5&31],e-=5;return e>0&&(i+=Ot[s<<5-e&31]),i},kt=t=>{t=t.replace(/ /g,"");const e=new ArrayBuffer(t.length/2),s=new Uint8Array(e);for(let e=0;e<t.length;e+=2)s[e/2]=parseInt(t.substring(e,e+2),16);return s},Tt=t=>{let e="";for(let s=0;s<t.length;s++){const i=t[s].toString(16);1===i.length&&(e+="0"),e+=i}return e.toUpperCase()},Dt=t=>{const e=new ArrayBuffer(t.length),s=new Uint8Array(e);for(let e=0;e<t.length;e++)s[e]=255&t.charCodeAt(e);return s},_t=t=>{let e="";for(let s=0;s<t.length;s++)e+=String.fromCharCode(t[s]);return e},Ft=Ut.TextEncoder?new Ut.TextEncoder:null,Gt=Ut.TextDecoder?new Ut.TextDecoder:null,Pt=t=>{if(!Ft)throw new Error("Encoding API not available");return Ft.encode(t)},jt=t=>{if(!Gt)throw new Error("Encoding API not available");return Gt.decode(t)};class Rt{static fromLatin1(t){return new Rt({buffer:Dt(t).buffer})}static fromUTF8(t){return new Rt({buffer:Pt(t).buffer})}static fromBase32(t){return new Rt({buffer:Ct(t).buffer})}static fromHex(t){return new Rt({buffer:kt(t).buffer})}get buffer(){return this.bytes.buffer}get latin1(){return Object.defineProperty(this,"latin1",{enumerable:!0,writable:!1,configurable:!1,value:_t(this.bytes)}),this.latin1}get utf8(){return Object.defineProperty(this,"utf8",{enumerable:!0,writable:!1,configurable:!1,value:jt(this.bytes)}),this.utf8}get base32(){return Object.defineProperty(this,"base32",{enumerable:!0,writable:!1,configurable:!1,value:$t(this.bytes)}),this.base32}get hex(){
return Object.defineProperty(this,"hex",{enumerable:!0,writable:!1,configurable:!1,value:Tt(this.bytes)}),this.hex}constructor({buffer:t,size:e=20}={}){this.bytes=void 0===t?(t=>{if(Ut.crypto?.getRandomValues)return Ut.crypto.getRandomValues(new Uint8Array(t));throw new Error("Cryptography API not available")})(e):new Uint8Array(t),Object.defineProperty(this,"bytes",{enumerable:!0,writable:!1,configurable:!1,value:this.bytes})}}class Mt{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,counter:0,window:1}}static generate({secret:t,algorithm:e=Mt.defaults.algorithm,digits:s=Mt.defaults.digits,counter:i=Mt.defaults.counter,hmac:r=vt}){const n=(t=>{const e=new ArrayBuffer(8),s=new Uint8Array(e);let i=t;for(let t=7;t>=0&&0!==i;t--)s[t]=255&i,i-=s[t],i/=256;return s})(i),o=r(e,t.bytes,n);if(!o?.byteLength||o.byteLength<19)throw new TypeError("Return value must be at least 19 bytes");const h=15&o[o.byteLength-1];return(((127&o[h])<<24|(255&o[h+1])<<16|(255&o[h+2])<<8|255&o[h+3])%10**s).toString().padStart(s,"0")}generate({counter:t=this.counter++}={}){return Mt.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:t,hmac:this.hmac})}static validate({token:t,secret:e,algorithm:s,digits:i=Mt.defaults.digits,counter:r=Mt.defaults.counter,window:n=Mt.defaults.window,hmac:o=vt}){if(t.length!==i)return null;let h=null;const a=n=>{const a=Mt.generate({secret:e,algorithm:s,digits:i,counter:n,hmac:o});((t,e)=>{{if(t.length!==e.length)throw new TypeError("Input strings must have the same length");let s=-1,i=0;for(;++s<t.length;)i|=t.charCodeAt(s)^e.charCodeAt(s);return 0===i}})(t,a)&&(h=n-r)};a(r);for(let t=1;t<=n&&null===h&&(a(r-t),null===h)&&(a(r+t),null===h);++t);return h}validate({token:t,counter:e=this.counter,window:s}){return Mt.validate({token:t,secret:this.secret,algorithm:this.algorithm,digits:this.digits,counter:e,window:s,hmac:this.hmac})}toString(){const t=encodeURIComponent
;return"otpauth://hotp/"+(this.issuer.length>0?this.issuerInLabel?`${t(this.issuer)}:${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?`)+`secret=${t(this.secret.base32)}&`+`algorithm=${t(this.algorithm)}&`+`digits=${t(this.digits)}&`+`counter=${t(this.counter)}`}constructor({issuer:t=Mt.defaults.issuer,label:e=Mt.defaults.label,issuerInLabel:s=Mt.defaults.issuerInLabel,secret:i=new Rt,algorithm:r=Mt.defaults.algorithm,digits:n=Mt.defaults.digits,counter:o=Mt.defaults.counter,hmac:h}={}){this.issuer=t,this.label=e,this.issuerInLabel=s,this.secret="string"==typeof i?Rt.fromBase32(i):i,this.algorithm=h?r:St(r),this.digits=n,this.counter=o,this.hmac=h}}class Xt{static get defaults(){return{issuer:"",label:"OTPAuth",issuerInLabel:!0,algorithm:"SHA1",digits:6,period:30,window:1}}static counter({period:t=Xt.defaults.period,timestamp:e=Date.now()}={}){return Math.floor(e/1e3/t)}counter({timestamp:t=Date.now()}={}){return Xt.counter({period:this.period,timestamp:t})}static remaining({period:t=Xt.defaults.period,timestamp:e=Date.now()}={}){return 1e3*t-e%(1e3*t)}remaining({timestamp:t=Date.now()}={}){return Xt.remaining({period:this.period,timestamp:t})}static generate({secret:t,algorithm:e,digits:s,period:i=Xt.defaults.period,timestamp:r=Date.now(),hmac:n}){return Mt.generate({secret:t,algorithm:e,digits:s,counter:Xt.counter({period:i,timestamp:r}),hmac:n})}generate({timestamp:t=Date.now()}={}){return Xt.generate({secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:t,hmac:this.hmac})}static validate({token:t,secret:e,algorithm:s,digits:i,period:r=Xt.defaults.period,timestamp:n=Date.now(),window:o,hmac:h}){return Mt.validate({token:t,secret:e,algorithm:s,digits:i,counter:Xt.counter({period:r,timestamp:n}),window:o,hmac:h})}validate({token:t,timestamp:e,window:s}){return Xt.validate({token:t,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:e,window:s,hmac:this.hmac})}toString(){
const t=encodeURIComponent;return"otpauth://totp/"+(this.issuer.length>0?this.issuerInLabel?`${t(this.issuer)}:${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?issuer=${t(this.issuer)}&`:`${t(this.label)}?`)+`secret=${t(this.secret.base32)}&`+`algorithm=${t(this.algorithm)}&`+`digits=${t(this.digits)}&`+`period=${t(this.period)}`}constructor({issuer:t=Xt.defaults.issuer,label:e=Xt.defaults.label,issuerInLabel:s=Xt.defaults.issuerInLabel,secret:i=new Rt,algorithm:r=Xt.defaults.algorithm,digits:n=Xt.defaults.digits,period:o=Xt.defaults.period,hmac:h}={}){this.issuer=t,this.label=e,this.issuerInLabel=s,this.secret="string"==typeof i?Rt.fromBase32(i):i,this.algorithm=h?r:St(r),this.digits=n,this.period=o,this.hmac=h}}const Nt=/^otpauth:\/\/([ht]otp)\/(.+)\?([A-Z0-9.~_-]+=[^?&]*(?:&[A-Z0-9.~_-]+=[^?&]*)*)$/i,Zt=/^[2-7A-Z]+=*$/i,Vt=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,zt=/^[A-Z0-9]+(?:[_-][A-Z0-9]+)*$/i,Jt=/^[+-]?\d+$/,Kt=/^\+?[1-9]\d*$/;t.HOTP=Mt,t.Secret=Rt,t.TOTP=Xt,t.URI=class{static parse(t,{hmac:e}={}){let s;try{s=t.match(Nt)}catch(t){}if(!Array.isArray(s))throw new URIError("Invalid URI format");const i=s[1].toLowerCase(),r=s[2].split(/(?::|%3A) *(.+)/i,2).map(decodeURIComponent),n=s[3].split("&").reduce((t,e)=>{const s=e.split(/=(.*)/,2).map(decodeURIComponent),i=s[0].toLowerCase(),r=s[1],n=t;return n[i]=r,n},{});let o;const h={};if("hotp"===i){if(o=Mt,void 0===n.counter||!Jt.test(n.counter))throw new TypeError("Missing or invalid 'counter' parameter");h.counter=parseInt(n.counter,10)}else{if("totp"!==i)throw new TypeError("Unknown OTP type");if(o=Xt,void 0!==n.period){if(!Kt.test(n.period))throw new TypeError("Invalid 'period' parameter");h.period=parseInt(n.period,10)}}if(void 0!==n.issuer&&(h.issuer=n.issuer),2===r.length?(h.label=r[1],void 0===h.issuer||""===h.issuer?h.issuer=r[0]:""===r[0]&&(h.issuerInLabel=!1)):(h.label=r[0],void 0!==h.issuer&&""!==h.issuer&&(h.issuerInLabel=!1)),
void 0===n.secret||!Zt.test(n.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(h.secret=n.secret,void 0!==n.algorithm){if(!(e?zt:Vt).test(n.algorithm))throw new TypeError("Invalid 'algorithm' parameter");h.algorithm=n.algorithm}if(void 0!==n.digits){if(!Kt.test(n.digits))throw new TypeError("Invalid 'digits' parameter");h.digits=parseInt(n.digits,10)}return void 0!==e&&(h.hmac=e),new o(h)}static stringify(t){if(t instanceof Mt||t instanceof Xt)return t.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}},t.version="9.5.0"});
//# sourceMappingURL=otpauth.umd.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,160 @@
{
"name": "otpauth",
"version": "9.5.0",
"description": "One Time Password (HOTP/TOTP) library for Node.js, Deno, Bun and browsers",
"keywords": [
"otp",
"hotp",
"totp",
"one time password",
"2fa",
"2 factor",
"two factor",
"two-factor",
"2step",
"2 step",
"two step",
"two-step",
"auth",
"authenticator",
"google authenticator"
],
"author": "Héctor Molinero Fernández <hector@molinero.dev>",
"license": "MIT",
"homepage": "https://github.com/hectorm/otpauth",
"repository": {
"type": "git",
"url": "https://github.com/hectorm/otpauth.git"
},
"bugs": {
"url": "https://github.com/hectorm/otpauth/issues"
},
"funding": "https://github.com/hectorm/otpauth?sponsor=1",
"type": "module",
"types": "./dist/otpauth.d.ts",
"main": "./dist/otpauth.node.cjs",
"browser": "./dist/otpauth.esm.js",
"exports": {
".": {
"types": {
"import": "./dist/otpauth.d.ts",
"require": "./dist/otpauth.d.cts"
},
"bun": "./dist/otpauth.esm.js",
"deno": "./dist/otpauth.esm.js",
"node": {
"import": "./dist/otpauth.node.mjs",
"require": "./dist/otpauth.node.cjs"
},
"default": "./dist/otpauth.esm.js"
},
"./slim": {
"types": {
"import": "./dist/otpauth.d.ts",
"require": "./dist/otpauth.d.cts"
},
"default": "./dist/otpauth.slim.esm.js"
},
"./bare": {
"types": {
"import": "./dist/otpauth.d.ts",
"require": "./dist/otpauth.d.cts"
},
"default": "./dist/otpauth.bare.esm.js"
},
"./dist/*": {
"types": {
"import": "./dist/otpauth.d.ts",
"require": "./dist/otpauth.d.cts"
},
"default": "./dist/*"
}
},
"files": [
"dist/"
],
"scripts": {
"all": "run-s build docs lint test",
"build": "run-s build:clean build:types build:compile",
"build:clean": "rimraf ./dist/",
"build:compile": "rollup -c",
"build:types": "run-s build:types:clean build:types:compile",
"build:types:clean": "rimraf ./types/",
"build:types:compile": "tsc -p ./src/",
"docs": "run-s docs:clean docs:compile",
"docs:clean": "rimraf ./docs/",
"docs:compile": "typedoc ./src/index.js --readme none --out ./docs/",
"lint": "run-s lint:eslint lint:prettier lint:tsc",
"lint:eslint": "eslint --max-warnings 0 ./",
"lint:prettier": "prettier --list-different ./",
"lint:tsc": "tsc --noEmit",
"format": "run-s format:eslint format:prettier",
"format:eslint": "eslint --fix ./",
"format:prettier": "prettier --write ./",
"test": "run-s test:node test:deno test:bun test:browser",
"test:node": "run-s test:node:esm test:node:cjs",
"test:node:esm": "run-s test:node:esm:unmin test:node:esm:min",
"test:node:esm:unmin": "node ./test/node.test.mjs",
"test:node:esm:min": "node ./test/node.test.min.mjs",
"test:node:cjs": "run-s test:node:cjs:unmin test:node:cjs:min",
"test:node:cjs:unmin": "node ./test/node.test.cjs",
"test:node:cjs:min": "node ./test/node.test.min.cjs",
"test:deno": "run-s --npm-path deno test:deno:esm",
"test:deno:esm": "run-s --npm-path deno test:deno:esm:unmin test:deno:esm:min",
"test:deno:esm:unmin": "deno test --no-npm --allow-read=./test/,./dist/ ./test/deno.test.mjs",
"test:deno:esm:min": "deno test --no-npm --allow-read=./test/,./dist/ ./test/deno.test.min.mjs",
"test:bun": "run-s --npm-path bun test:bun:esm",
"test:bun:esm": "run-s --npm-path bun test:bun:esm:unmin test:bun:esm:min",
"test:bun:esm:unmin": "bun test ./test/bun.test.mjs",
"test:bun:esm:min": "bun test ./test/bun.test.min.mjs ",
"test:quickjs": "run-s test:quickjs:esm",
"test:quickjs:esm": "run-s test:quickjs:esm:unmin test:quickjs:esm:min",
"test:quickjs:esm:unmin": "qjs ./test/quickjs.test.mjs",
"test:quickjs:esm:min": "qjs ./test/quickjs.test.min.mjs ",
"test:browser": "run-s test:browser:chromium test:browser:firefox test:browser:webkit",
"test:browser:chromium": "run-s test:browser:chromium:umd",
"test:browser:chromium:umd": "run-s test:browser:chromium:umd:unmin test:browser:chromium:umd:min",
"test:browser:chromium:umd:unmin": "node ./test/browser.test.mjs chromium",
"test:browser:chromium:umd:min": "node ./test/browser.test.min.mjs chromium",
"test:browser:firefox": "run-s test:browser:firefox:umd",
"test:browser:firefox:umd": "run-s test:browser:firefox:umd:unmin test:browser:firefox:umd:min",
"test:browser:firefox:umd:unmin": "node ./test/browser.test.mjs firefox",
"test:browser:firefox:umd:min": "node ./test/browser.test.min.mjs firefox",
"test:browser:webkit": "run-s test:browser:webkit:umd",
"test:browser:webkit:umd": "run-s test:browser:webkit:umd:unmin test:browser:webkit:umd:min",
"test:browser:webkit:umd:unmin": "node ./test/browser.test.mjs webkit",
"test:browser:webkit:umd:min": "node ./test/browser.test.min.mjs webkit",
"version": "run-s all version:jsr version:git",
"version:jsr": "node ./scripts/jsr.mjs",
"version:git": "git add -A ./jsr.json ./types/ ./dist/ ./docs/"
},
"dependencies": {
"@noble/hashes": "2.0.1"
},
"devDependencies": {
"@eslint/core": "~1.1.0",
"@eslint/js": "~9.39.2",
"@rollup/plugin-node-resolve": "~16.0.3",
"@rollup/plugin-replace": "~6.0.3",
"@rollup/plugin-swc": "~0.4.0",
"@rollup/plugin-terser": "~0.4.4",
"@rollup/plugin-virtual": "~3.0.2",
"@swc/core": "~1.15.11",
"@types/eslint": "~9.6.1",
"@types/eslint-config-prettier": "~6.11.3",
"@types/node": "~25.2.0",
"chai": "~6.2.2",
"eslint": "~9.39.2",
"eslint-config-prettier": "~10.1.8",
"globals": "~17.3.0",
"mocha": "~11.7.5",
"npm-run-all2": "~8.0.4",
"playwright": "~1.58.1",
"prettier": "~3.8.1",
"rimraf": "~6.1.2",
"rollup": "~4.57.1",
"rollup-plugin-dts": "~6.3.0",
"typedoc": "~0.28.16",
"typescript": "~5.9.3"
}
}