//! otpauth 9.3.1 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth
//! noble-hashes 1.4.0 | (c) Paul Miller | MIT | https://github.com/paulmillr/noble-hashes
///
// @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;
};
function number(n) {
if (!Number.isSafeInteger(n) || n < 0) throw new Error(`positive integer expected, not ${n}`);
}
// copied from utils
function isBytes(a) {
return a instanceof Uint8Array || a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array';
}
function bytes(b, ...lengths) {
if (!isBytes(b)) throw new Error('Uint8Array expected');
if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`);
}
function hash(h) {
if (typeof h !== 'function' || typeof h.create !== 'function') throw new Error('Hash should be wrapped by utils.wrapConstructor');
number(h.outputLen);
number(h.blockLen);
}
function exists(instance, checkFinished = true) {
if (instance.destroyed) throw new Error('Hash instance has been destroyed');
if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');
}
function output(out, instance) {
bytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
}
}
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ // We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// node.js versions earlier than v19 don't declare it in global scope.
// For node.js, package.json#exports field mapping rewrites import
// from `crypto` to `cryptoNode`, which imports native module.
// Makes the utils un-importable in browsers without a bundler.
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
const u32 = (arr)=>new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
// Cast array to view
const createView = (arr)=>new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
// The rotate right (circular right shift) operation for uint32
const rotr = (word, shift)=>word << 32 - shift | word >>> shift;
// The rotate left (circular left shift) operation for uint32
const rotl = (word, shift)=>word << shift | word >>> 32 - shift >>> 0;
const isLE = new Uint8Array(new Uint32Array([
0x11223344
]).buffer)[0] === 0x44;
// The byte swap operation for uint32
const byteSwap = (word)=>word << 24 & 0xff000000 | word << 8 & 0xff0000 | word >>> 8 & 0xff00 | word >>> 24 & 0xff;
// In place byte swap for Uint32Array
function byteSwap32(arr) {
for(let i = 0; i < arr.length; i++){
arr[i] = byteSwap(arr[i]);
}
}
/**
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
*/ function utf8ToBytes(str) {
if (typeof str !== 'string') throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
}
/**
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
* Warning: when Uint8Array is passed, it would NOT get copied.
* Keep in mind for future mutable operations.
*/ function toBytes(data) {
if (typeof data === 'string') data = utf8ToBytes(data);
bytes(data);
return data;
}
// For runtime check if class implements interface
class Hash {
// Safe version that clones internal state
clone() {
return this._cloneInto();
}
}
function wrapConstructor(hashCons) {
const hashC = (msg)=>hashCons().update(toBytes(msg)).digest();
const tmp = hashCons();
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = ()=>hashCons();
return hashC;
}
// HMAC (RFC 2104)
class HMAC extends Hash {
update(buf) {
exists(this);
this.iHash.update(buf);
return this;
}
digestInto(out) {
exists(this);
bytes(out, this.outputLen);
this.finished = true;
this.iHash.digestInto(out);
this.oHash.update(out);
this.oHash.digestInto(out);
this.destroy();
}
digest() {
const out = new Uint8Array(this.oHash.outputLen);
this.digestInto(out);
return out;
}
_cloneInto(to) {
// Create new instance without calling constructor since key already in state and we don't know it.
to || (to = Object.create(Object.getPrototypeOf(this), {}));
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
to = to;
to.finished = finished;
to.destroyed = destroyed;
to.blockLen = blockLen;
to.outputLen = outputLen;
to.oHash = oHash._cloneInto(to.oHash);
to.iHash = iHash._cloneInto(to.iHash);
return to;
}
destroy() {
this.destroyed = true;
this.oHash.destroy();
this.iHash.destroy();
}
constructor(hash$1, _key){
super();
this.finished = false;
this.destroyed = false;
hash(hash$1);
const key = toBytes(_key);
this.iHash = hash$1.create();
if (typeof this.iHash.update !== 'function') throw new Error('Expected instance of class which extends utils.Hash');
this.blockLen = this.iHash.blockLen;
this.outputLen = this.iHash.outputLen;
const blockLen = this.blockLen;
const pad = new Uint8Array(blockLen);
// blockLen can be bigger than outputLen
pad.set(key.length > blockLen ? hash$1.create().update(key).digest() : key);
for(let i = 0; i < pad.length; i++)pad[i] ^= 0x36;
this.iHash.update(pad);
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
this.oHash = hash$1.create();
// Undo internal XOR && apply outer XOR
for(let i = 0; i < pad.length; i++)pad[i] ^= 0x36 ^ 0x5c;
this.oHash.update(pad);
pad.fill(0);
}
}
/**
* HMAC: RFC2104 message authentication code.
* @param hash - function that would be used e.g. sha256
* @param key - message key
* @param message - message data
*/ const hmac = (hash, key, message)=>new HMAC(hash, key).update(message).digest();
hmac.create = (hash, key)=>new HMAC(hash, key);
// Polyfill for Safari 14
function setBigUint64(view, byteOffset, value, isLE) {
if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
const wh = Number(value >> _32n & _u32_max);
const wl = Number(value & _u32_max);
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
// Choice: a ? b : c
const Chi = (a, b, c)=>a & b ^ ~a & c;
// Majority function, true if any two inpust is true
const Maj = (a, b, c)=>a & b ^ a & c ^ b & c;
/**
* Merkle-Damgard hash construction base class.
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
*/ class HashMD extends Hash {
update(data) {
exists(this);
const { view, buffer, blockLen } = this;
data = toBytes(data);
const len = data.length;
for(let pos = 0; pos < len;){
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input, cast it to view and process
if (take === blockLen) {
const dataView = createView(data);
for(; blockLen <= len - pos; pos += blockLen)this.process(dataView, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(view, 0);
this.pos = 0;
}
}
this.length += data.length;
this.roundClean();
return this;
}
digestInto(out) {
exists(this);
output(out, this);
this.finished = true;
// Padding
// We can avoid allocation of buffer for padding completely if it
// was previously not allocated here. But it won't change performance.
const { buffer, view, blockLen, isLE } = this;
let { pos } = this;
// append the bit '1' to the message
buffer[pos++] = 0b10000000;
this.buffer.subarray(pos).fill(0);
// we have less than padOffset left in buffer, so we cannot put length in
// current block, need process it and pad again
if (this.padOffset > blockLen - pos) {
this.process(view, 0);
pos = 0;
}
// Pad until full block byte with zeros
for(let i = pos; i < blockLen; i++)buffer[i] = 0;
// Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
// So we just write lowest 64 bits of that value.
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
this.process(view, 0);
const oview = createView(out);
const len = this.outputLen;
// NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
if (len % 4) throw new Error('_sha2: outputLen should be aligned to 32bit');
const outLen = len / 4;
const state = this.get();
if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');
for(let i = 0; i < outLen; i++)oview.setUint32(4 * i, state[i], isLE);
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
_cloneInto(to) {
to || (to = new this.constructor());
to.set(...this.get());
const { blockLen, buffer, length, finished, destroyed, pos } = this;
to.length = length;
to.pos = pos;
to.finished = finished;
to.destroyed = destroyed;
if (length % blockLen) to.buffer.set(buffer);
return to;
}
constructor(blockLen, outputLen, padOffset, isLE){
super();
this.blockLen = blockLen;
this.outputLen = outputLen;
this.padOffset = padOffset;
this.isLE = isLE;
this.finished = false;
this.length = 0;
this.pos = 0;
this.destroyed = false;
this.buffer = new Uint8Array(blockLen);
this.view = createView(this.buffer);
}
}
// SHA1 (RFC 3174) was cryptographically broken. It's still used. Don't use it for a new protocol.
// Initial state
const SHA1_IV = /* @__PURE__ */ new Uint32Array([
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
0xc3d2e1f0
]);
// Temporary buffer, not used to store anything between runs
// Named this way because it matches specification.
const SHA1_W = /* @__PURE__ */ new Uint32Array(80);
class SHA1 extends HashMD {
get() {
const { A, B, C, D, E } = this;
return [
A,
B,
C,
D,
E
];
}
set(A, B, C, D, E) {
this.A = A | 0;
this.B = B | 0;
this.C = C | 0;
this.D = D | 0;
this.E = E | 0;
}
process(view, offset) {
for(let i = 0; i < 16; i++, offset += 4)SHA1_W[i] = view.getUint32(offset, false);
for(let i = 16; i < 80; i++)SHA1_W[i] = rotl(SHA1_W[i - 3] ^ SHA1_W[i - 8] ^ SHA1_W[i - 14] ^ SHA1_W[i - 16], 1);
// Compression function main loop, 80 rounds
let { A, B, C, D, E } = this;
for(let i = 0; i < 80; i++){
let F, K;
if (i < 20) {
F = Chi(B, C, D);
K = 0x5a827999;
} else if (i < 40) {
F = B ^ C ^ D;
K = 0x6ed9eba1;
} else if (i < 60) {
F = Maj(B, C, D);
K = 0x8f1bbcdc;
} else {
F = B ^ C ^ D;
K = 0xca62c1d6;
}
const T = rotl(A, 5) + F + E + K + SHA1_W[i] | 0;
E = D;
D = C;
C = rotl(B, 30);
B = A;
A = T;
}
// Add the compressed chunk to the current hash value
A = A + this.A | 0;
B = B + this.B | 0;
C = C + this.C | 0;
D = D + this.D | 0;
E = E + this.E | 0;
this.set(A, B, C, D, E);
}
roundClean() {
SHA1_W.fill(0);
}
destroy() {
this.set(0, 0, 0, 0, 0);
this.buffer.fill(0);
}
constructor(){
super(64, 20, 8, false);
this.A = SHA1_IV[0] | 0;
this.B = SHA1_IV[1] | 0;
this.C = SHA1_IV[2] | 0;
this.D = SHA1_IV[3] | 0;
this.E = SHA1_IV[4] | 0;
}
}
const sha1 = /* @__PURE__ */ wrapConstructor(()=>new SHA1());
// SHA2-256 need to try 2^128 hashes to execute birthday attack.
// BTC network is doing 2^67 hashes/sec as per early 2023.
// Round constants:
// first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
// prettier-ignore
const SHA256_K = /* @__PURE__ */ new Uint32Array([
0x428a2f98,
0x71374491,
0xb5c0fbcf,
0xe9b5dba5,
0x3956c25b,
0x59f111f1,
0x923f82a4,
0xab1c5ed5,
0xd807aa98,
0x12835b01,
0x243185be,
0x550c7dc3,
0x72be5d74,
0x80deb1fe,
0x9bdc06a7,
0xc19bf174,
0xe49b69c1,
0xefbe4786,
0x0fc19dc6,
0x240ca1cc,
0x2de92c6f,
0x4a7484aa,
0x5cb0a9dc,
0x76f988da,
0x983e5152,
0xa831c66d,
0xb00327c8,
0xbf597fc7,
0xc6e00bf3,
0xd5a79147,
0x06ca6351,
0x14292967,
0x27b70a85,
0x2e1b2138,
0x4d2c6dfc,
0x53380d13,
0x650a7354,
0x766a0abb,
0x81c2c92e,
0x92722c85,
0xa2bfe8a1,
0xa81a664b,
0xc24b8b70,
0xc76c51a3,
0xd192e819,
0xd6990624,
0xf40e3585,
0x106aa070,
0x19a4c116,
0x1e376c08,
0x2748774c,
0x34b0bcb5,
0x391c0cb3,
0x4ed8aa4a,
0x5b9cca4f,
0x682e6ff3,
0x748f82ee,
0x78a5636f,
0x84c87814,
0x8cc70208,
0x90befffa,
0xa4506ceb,
0xbef9a3f7,
0xc67178f2
]);
// Initial state:
// first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
// prettier-ignore
const SHA256_IV = /* @__PURE__ */ new Uint32Array([
0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19
]);
// Temporary buffer, not used to store anything between runs
// Named this way because it matches specification.
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
class SHA256 extends HashMD {
get() {
const { A, B, C, D, E, F, G, H } = this;
return [
A,
B,
C,
D,
E,
F,
G,
H
];
}
// prettier-ignore
set(A, B, C, D, E, F, G, H) {
this.A = A | 0;
this.B = B | 0;
this.C = C | 0;
this.D = D | 0;
this.E = E | 0;
this.F = F | 0;
this.G = G | 0;
this.H = H | 0;
}
process(view, offset) {
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
for(let i = 0; i < 16; i++, offset += 4)SHA256_W[i] = view.getUint32(offset, false);
for(let i = 16; i < 64; i++){
const W15 = SHA256_W[i - 15];
const W2 = SHA256_W[i - 2];
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
}
// Compression function main loop, 64 rounds
let { A, B, C, D, E, F, G, H } = this;
for(let i = 0; i < 64; i++){
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
const T2 = sigma0 + Maj(A, B, C) | 0;
H = G;
G = F;
F = E;
E = D + T1 | 0;
D = C;
C = B;
B = A;
A = T1 + T2 | 0;
}
// Add the compressed chunk to the current hash value
A = A + this.A | 0;
B = B + this.B | 0;
C = C + this.C | 0;
D = D + this.D | 0;
E = E + this.E | 0;
F = F + this.F | 0;
G = G + this.G | 0;
H = H + this.H | 0;
this.set(A, B, C, D, E, F, G, H);
}
roundClean() {
SHA256_W.fill(0);
}
destroy() {
this.set(0, 0, 0, 0, 0, 0, 0, 0);
this.buffer.fill(0);
}
constructor(){
super(64, 32, 8, false);
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
this.A = SHA256_IV[0] | 0;
this.B = SHA256_IV[1] | 0;
this.C = SHA256_IV[2] | 0;
this.D = SHA256_IV[3] | 0;
this.E = SHA256_IV[4] | 0;
this.F = SHA256_IV[5] | 0;
this.G = SHA256_IV[6] | 0;
this.H = SHA256_IV[7] | 0;
}
}
// Constants from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
class SHA224 extends SHA256 {
constructor(){
super();
this.A = 0xc1059ed8 | 0;
this.B = 0x367cd507 | 0;
this.C = 0x3070dd17 | 0;
this.D = 0xf70e5939 | 0;
this.E = 0xffc00b31 | 0;
this.F = 0x68581511 | 0;
this.G = 0x64f98fa7 | 0;
this.H = 0xbefa4fa4 | 0;
this.outputLen = 28;
}
}
/**
* SHA2-256 hash function
* @param message - data that would be hashed
*/ const sha256 = /* @__PURE__ */ wrapConstructor(()=>new SHA256());
const sha224 = /* @__PURE__ */ wrapConstructor(()=>new SHA224());
const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
const _32n = /* @__PURE__ */ BigInt(32);
// We are not using BigUint64Array, because they are extremely slow as per 2022
function fromBig(n, le = false) {
if (le) return {
h: Number(n & U32_MASK64),
l: Number(n >> _32n & U32_MASK64)
};
return {
h: Number(n >> _32n & U32_MASK64) | 0,
l: Number(n & U32_MASK64) | 0
};
}
function split(lst, le = false) {
let Ah = new Uint32Array(lst.length);
let Al = new Uint32Array(lst.length);
for(let i = 0; i < lst.length; i++){
const { h, l } = fromBig(lst[i], le);
[Ah[i], Al[i]] = [
h,
l
];
}
return [
Ah,
Al
];
}
const toBig = (h, l)=>BigInt(h >>> 0) << _32n | BigInt(l >>> 0);
// for Shift in [0, 32)
const shrSH = (h, _l, s)=>h >>> s;
const shrSL = (h, l, s)=>h << 32 - s | l >>> s;
// Right rotate for Shift in [1, 32)
const rotrSH = (h, l, s)=>h >>> s | l << 32 - s;
const rotrSL = (h, l, s)=>h << 32 - s | l >>> s;
// Right rotate for Shift in (32, 64), NOTE: 32 is special case.
const rotrBH = (h, l, s)=>h << 64 - s | l >>> s - 32;
const rotrBL = (h, l, s)=>h >>> s - 32 | l << 64 - s;
// Right rotate for shift===32 (just swaps l&h)
const rotr32H = (_h, l)=>l;
const rotr32L = (h, _l)=>h;
// Left rotate for Shift in [1, 32)
const rotlSH = (h, l, s)=>h << s | l >>> 32 - s;
const rotlSL = (h, l, s)=>l << s | h >>> 32 - s;
// Left rotate for Shift in (32, 64), NOTE: 32 is special case.
const rotlBH = (h, l, s)=>l << s - 32 | h >>> 64 - s;
const rotlBL = (h, l, s)=>h << s - 32 | l >>> 64 - s;
// JS uses 32-bit signed integers for bitwise operations which means we cannot
// simple take carry out of low bit sum by shift, we need to use division.
function add(Ah, Al, Bh, Bl) {
const l = (Al >>> 0) + (Bl >>> 0);
return {
h: Ah + Bh + (l / 2 ** 32 | 0) | 0,
l: l | 0
};
}
// Addition with more than 2 elements
const add3L = (Al, Bl, Cl)=>(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
const add3H = (low, Ah, Bh, Ch)=>Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0;
const add4L = (Al, Bl, Cl, Dl)=>(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
const add4H = (low, Ah, Bh, Ch, Dh)=>Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0;
const add5L = (Al, Bl, Cl, Dl, El)=>(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
const add5H = (low, Ah, Bh, Ch, Dh, Eh)=>Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
// prettier-ignore
const u64 = {
fromBig,
split,
toBig,
shrSH,
shrSL,
rotrSH,
rotrSL,
rotrBH,
rotrBL,
rotr32H,
rotr32L,
rotlSH,
rotlSL,
rotlBH,
rotlBL,
add,
add3L,
add3H,
add4L,
add4H,
add5H,
add5L
};
// Round contants (first 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409):
// prettier-ignore
const [SHA512_Kh, SHA512_Kl] = /* @__PURE__ */ (()=>u64.split([
'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((n)=>BigInt(n))))();
// Temporary buffer, not used to store anything between runs
const SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);
const SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);
class SHA512 extends HashMD {
// prettier-ignore
get() {
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
return [
Ah,
Al,
Bh,
Bl,
Ch,
Cl,
Dh,
Dl,
Eh,
El,
Fh,
Fl,
Gh,
Gl,
Hh,
Hl
];
}
// prettier-ignore
set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {
this.Ah = Ah | 0;
this.Al = Al | 0;
this.Bh = Bh | 0;
this.Bl = Bl | 0;
this.Ch = Ch | 0;
this.Cl = Cl | 0;
this.Dh = Dh | 0;
this.Dl = Dl | 0;
this.Eh = Eh | 0;
this.El = El | 0;
this.Fh = Fh | 0;
this.Fl = Fl | 0;
this.Gh = Gh | 0;
this.Gl = Gl | 0;
this.Hh = Hh | 0;
this.Hl = Hl | 0;
}
process(view, offset) {
// Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array
for(let i = 0; i < 16; i++, offset += 4){
SHA512_W_H[i] = view.getUint32(offset);
SHA512_W_L[i] = view.getUint32(offset += 4);
}
for(let i = 16; i < 80; i++){
// s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)
const W15h = SHA512_W_H[i - 15] | 0;
const W15l = SHA512_W_L[i - 15] | 0;
const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);
const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);
// s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)
const W2h = SHA512_W_H[i - 2] | 0;
const W2l = SHA512_W_L[i - 2] | 0;
const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);
const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);
// SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16];
const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
SHA512_W_H[i] = SUMh | 0;
SHA512_W_L[i] = SUMl | 0;
}
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
// Compression function main loop, 80 rounds
for(let i = 0; i < 80; i++){
// S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)
const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);
const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);
//const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
const CHIh = Eh & Fh ^ ~Eh & Gh;
const CHIl = El & Fl ^ ~El & Gl;
// T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]
// prettier-ignore
const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
const T1l = T1ll | 0;
// S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)
const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);
const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);
const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch;
const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl;
Hh = Gh | 0;
Hl = Gl | 0;
Gh = Fh | 0;
Gl = Fl | 0;
Fh = Eh | 0;
Fl = El | 0;
({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
Dh = Ch | 0;
Dl = Cl | 0;
Ch = Bh | 0;
Cl = Bl | 0;
Bh = Ah | 0;
Bl = Al | 0;
const All = u64.add3L(T1l, sigma0l, MAJl);
Ah = u64.add3H(All, T1h, sigma0h, MAJh);
Al = All | 0;
}
// Add the compressed chunk to the current hash value
({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
}
roundClean() {
SHA512_W_H.fill(0);
SHA512_W_L.fill(0);
}
destroy() {
this.buffer.fill(0);
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
constructor(){
super(128, 64, 16, false);
// We cannot use array here since array allows indexing by variable which means optimizer/compiler cannot use registers.
// Also looks cleaner and easier to verify with spec.
// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
// h -- high 32 bits, l -- low 32 bits
this.Ah = 0x6a09e667 | 0;
this.Al = 0xf3bcc908 | 0;
this.Bh = 0xbb67ae85 | 0;
this.Bl = 0x84caa73b | 0;
this.Ch = 0x3c6ef372 | 0;
this.Cl = 0xfe94f82b | 0;
this.Dh = 0xa54ff53a | 0;
this.Dl = 0x5f1d36f1 | 0;
this.Eh = 0x510e527f | 0;
this.El = 0xade682d1 | 0;
this.Fh = 0x9b05688c | 0;
this.Fl = 0x2b3e6c1f | 0;
this.Gh = 0x1f83d9ab | 0;
this.Gl = 0xfb41bd6b | 0;
this.Hh = 0x5be0cd19 | 0;
this.Hl = 0x137e2179 | 0;
}
}
class SHA384 extends SHA512 {
constructor(){
super();
// h -- high 32 bits, l -- low 32 bits
this.Ah = 0xcbbb9d5d | 0;
this.Al = 0xc1059ed8 | 0;
this.Bh = 0x629a292a | 0;
this.Bl = 0x367cd507 | 0;
this.Ch = 0x9159015a | 0;
this.Cl = 0x3070dd17 | 0;
this.Dh = 0x152fecd8 | 0;
this.Dl = 0xf70e5939 | 0;
this.Eh = 0x67332667 | 0;
this.El = 0xffc00b31 | 0;
this.Fh = 0x8eb44a87 | 0;
this.Fl = 0x68581511 | 0;
this.Gh = 0xdb0c2e0d | 0;
this.Gl = 0x64f98fa7 | 0;
this.Hh = 0x47b5481d | 0;
this.Hl = 0xbefa4fa4 | 0;
this.outputLen = 48;
}
}
const sha512 = /* @__PURE__ */ wrapConstructor(()=>new SHA512());
const sha384 = /* @__PURE__ */ wrapConstructor(()=>new SHA384());
// SHA3 (keccak) is based on a new design: basically, the internal state is bigger than output size.
// It's called a sponge function.
// Various per round constants calculations
const SHA3_PI = [];
const SHA3_ROTL = [];
const _SHA3_IOTA = [];
const _0n = /* @__PURE__ */ BigInt(0);
const _1n = /* @__PURE__ */ BigInt(1);
const _2n = /* @__PURE__ */ BigInt(2);
const _7n = /* @__PURE__ */ BigInt(7);
const _256n = /* @__PURE__ */ BigInt(256);
const _0x71n = /* @__PURE__ */ BigInt(0x71);
for(let round = 0, R = _1n, x = 1, y = 0; round < 24; round++){
// Pi
[x, y] = [
y,
(2 * x + 3 * y) % 5
];
SHA3_PI.push(2 * (5 * y + x));
// Rotational
SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
// Iota
let t = _0n;
for(let j = 0; j < 7; j++){
R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
if (R & _2n) t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
}
_SHA3_IOTA.push(t);
}
const [SHA3_IOTA_H, SHA3_IOTA_L] = /* @__PURE__ */ split(_SHA3_IOTA, true);
// Left rotation (without 0, 32, 64)
const rotlH = (h, l, s)=>s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
const rotlL = (h, l, s)=>s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
// Same as keccakf1600, but allows to skip some rounds
function keccakP(s, rounds = 24) {
const B = new Uint32Array(5 * 2);
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
for(let round = 24 - rounds; round < 24; round++){
// Theta θ
for(let x = 0; x < 10; x++)B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
for(let x = 0; x < 10; x += 2){
const idx1 = (x + 8) % 10;
const idx0 = (x + 2) % 10;
const B0 = B[idx0];
const B1 = B[idx0 + 1];
const Th = rotlH(B0, B1, 1) ^ B[idx1];
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
for(let y = 0; y < 50; y += 10){
s[x + y] ^= Th;
s[x + y + 1] ^= Tl;
}
}
// Rho (ρ) and Pi (π)
let curH = s[2];
let curL = s[3];
for(let t = 0; t < 24; t++){
const shift = SHA3_ROTL[t];
const Th = rotlH(curH, curL, shift);
const Tl = rotlL(curH, curL, shift);
const PI = SHA3_PI[t];
curH = s[PI];
curL = s[PI + 1];
s[PI] = Th;
s[PI + 1] = Tl;
}
// Chi (χ)
for(let y = 0; y < 50; y += 10){
for(let x = 0; x < 10; x++)B[x] = s[y + x];
for(let x = 0; x < 10; x++)s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
}
// Iota (ι)
s[0] ^= SHA3_IOTA_H[round];
s[1] ^= SHA3_IOTA_L[round];
}
B.fill(0);
}
class Keccak extends Hash {
keccak() {
if (!isLE) byteSwap32(this.state32);
keccakP(this.state32, this.rounds);
if (!isLE) byteSwap32(this.state32);
this.posOut = 0;
this.pos = 0;
}
update(data) {
exists(this);
const { blockLen, state } = this;
data = toBytes(data);
const len = data.length;
for(let pos = 0; pos < len;){
const take = Math.min(blockLen - this.pos, len - pos);
for(let i = 0; i < take; i++)state[this.pos++] ^= data[pos++];
if (this.pos === blockLen) this.keccak();
}
return this;
}
finish() {
if (this.finished) return;
this.finished = true;
const { state, suffix, pos, blockLen } = this;
// Do the padding
state[pos] ^= suffix;
if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak();
state[blockLen - 1] ^= 0x80;
this.keccak();
}
writeInto(out) {
exists(this, false);
bytes(out);
this.finish();
const bufferOut = this.state;
const { blockLen } = this;
for(let pos = 0, len = out.length; pos < len;){
if (this.posOut >= blockLen) this.keccak();
const take = Math.min(blockLen - this.posOut, len - pos);
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
this.posOut += take;
pos += take;
}
return out;
}
xofInto(out) {
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
if (!this.enableXOF) throw new Error('XOF is not possible for this instance');
return this.writeInto(out);
}
xof(bytes) {
number(bytes);
return this.xofInto(new Uint8Array(bytes));
}
digestInto(out) {
output(out, this);
if (this.finished) throw new Error('digest() was already called');
this.writeInto(out);
this.destroy();
return out;
}
digest() {
return this.digestInto(new Uint8Array(this.outputLen));
}
destroy() {
this.destroyed = true;
this.state.fill(0);
}
_cloneInto(to) {
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
to.state32.set(this.state32);
to.pos = this.pos;
to.posOut = this.posOut;
to.finished = this.finished;
to.rounds = rounds;
// Suffix can change in cSHAKE
to.suffix = suffix;
to.outputLen = outputLen;
to.enableXOF = enableXOF;
to.destroyed = this.destroyed;
return to;
}
// NOTE: we accept arguments in bytes instead of bits here.
constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24){
super();
this.blockLen = blockLen;
this.suffix = suffix;
this.outputLen = outputLen;
this.enableXOF = enableXOF;
this.rounds = rounds;
this.pos = 0;
this.posOut = 0;
this.finished = false;
this.destroyed = false;
// Can be passed from user as dkLen
number(outputLen);
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
if (0 >= this.blockLen || this.blockLen >= 200) throw new Error('Sha3 supports only keccak-f1600 function');
this.state = new Uint8Array(200);
this.state32 = u32(this.state);
}
}
const gen = (suffix, blockLen, outputLen)=>wrapConstructor(()=>new Keccak(blockLen, suffix, outputLen));
const sha3_224 = /* @__PURE__ */ gen(0x06, 144, 224 / 8);
/**
* SHA3-256 hash function
* @param message - that would be hashed
*/ const sha3_256 = /* @__PURE__ */ gen(0x06, 136, 256 / 8);
const sha3_384 = /* @__PURE__ */ gen(0x06, 104, 384 / 8);
const sha3_512 = /* @__PURE__ */ gen(0x06, 72, 512 / 8);
/**
* "globalThis" ponyfill.
* @see [A horrifying globalThis polyfill in universal JavaScript](https://mathiasbynens.be/notes/globalthis)
* @type {Object.}
*/ const globalScope = (()=>{
if (typeof globalThis === "object") return globalThis;
else {
Object.defineProperty(Object.prototype, "__GLOBALTHIS__", {
get () {
return this;
},
configurable: true
});
try {
// @ts-ignore
// eslint-disable-next-line no-undef
if (typeof __GLOBALTHIS__ !== "undefined") return __GLOBALTHIS__;
} finally{
// @ts-ignore
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;
})();
/**
* OpenSSL-Noble hashes map.
* @type {Object.}
*/ const OPENSSL_NOBLE_HASHES = {
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
};
/**
* Calculates an HMAC digest.
* In Node.js, the command "openssl list -digest-algorithms" displays the available digest algorithms.
* @param {string} algorithm Algorithm.
* @param {Uint8Array} key Key.
* @param {Uint8Array} message Message.
* @returns {Uint8Array} Digest.
*/ const hmacDigest = (algorithm, key, message)=>{
if (hmac) {
const hash = OPENSSL_NOBLE_HASHES[algorithm.toUpperCase()];
if (!hash) throw new TypeError("Unknown hash function");
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)=>{
// Canonicalize to all upper case and remove padding if it exists.
let end = str.length;
while(str[end - 1] === "=")--end;
const cstr = (end < str.length ? str.substring(0, end) : str).toUpperCase();
const buf = new ArrayBuffer(cstr.length * 5 / 8 | 0);
const arr = new Uint8Array(buf);
let bits = 0;
let value = 0;
let index = 0;
for(let i = 0; i < cstr.length; i++){
const idx = ALPHABET.indexOf(cstr[i]);
if (idx === -1) throw new TypeError(`Invalid character found: ${cstr[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)=>{
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) {
throw new Error("Cryptography API not available");
}
return globalScope.crypto.getRandomValues(new Uint8Array(size));
}
};
/**
* 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://tools.ietf.org/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.
* @returns {string} Token.
*/ static generate({ secret, algorithm = HOTP.defaults.algorithm, digits = HOTP.defaults.digits, counter = HOTP.defaults.counter }) {
const digest = hmacDigest(algorithm, secret.bytes, uintDecode(counter));
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
});
}
/**
* 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 Token length.
* @param {number} [config.counter=0] 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.
*/ static validate({ token, secret, algorithm, digits, counter = HOTP.defaults.counter, window = HOTP.defaults.window }) {
// 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
});
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
});
}
/**
* 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.
*/ 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 } = {}){
/**
* 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 = algorithm.toUpperCase();
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Initial counter value.
* @type {number}
*/ this.counter = counter;
}
}
/**
* TOTP: Time-Based One-Time Password Algorithm.
* @see [RFC 6238](https://tools.ietf.org/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
};
}
/**
* 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.
* @returns {string} Token.
*/ static generate({ secret, algorithm, digits, period = TOTP.defaults.period, timestamp = Date.now() }) {
return HOTP.generate({
secret,
algorithm,
digits,
counter: Math.floor(timestamp / 1000 / period)
});
}
/**
* 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
});
}
/**
* 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 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.
* @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 }) {
return HOTP.validate({
token,
secret,
algorithm,
digits,
counter: Math.floor(timestamp / 1000 / period),
window
});
}
/**
* 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
});
}
/**
* 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.
*/ 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 } = {}){
/**
* 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 = algorithm.toUpperCase();
/**
* Token length.
* @type {number}
*/ this.digits = digits;
/**
* Token time-step duration.
* @type {number}
*/ this.period = period;
}
}
/**
* 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.
* @type {RegExp}
*/ const ALGORITHM_REGEX = /^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/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.
* @returns {HOTP|TOTP} HOTP/TOTP object.
*/ static parse(uri) {
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.} */ 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.} */ 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 (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");
}
}
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.3.1";
export { HOTP, Secret, TOTP, URI, version };