altcha 3.0.0-beta.2 → 3.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,111 @@
1
+ const noop = () => {
2
+ };
3
+ function safe_not_equal(a, b) {
4
+ return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function";
5
+ }
6
+ function subscribe_to_store(store2, run, invalidate) {
7
+ if (store2 == null) {
8
+ run(void 0);
9
+ return noop;
10
+ }
11
+ const unsub = untrack(
12
+ () => store2.subscribe(
13
+ run,
14
+ // @ts-expect-error
15
+ invalidate
16
+ )
17
+ );
18
+ return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
19
+ }
20
+ const subscriber_queue = [];
21
+ function writable(value, start = noop) {
22
+ let stop = null;
23
+ const subscribers = /* @__PURE__ */ new Set();
24
+ function set(new_value) {
25
+ if (safe_not_equal(value, new_value)) {
26
+ value = new_value;
27
+ if (stop) {
28
+ const run_queue = !subscriber_queue.length;
29
+ for (const subscriber of subscribers) {
30
+ subscriber[1]();
31
+ subscriber_queue.push(subscriber, value);
32
+ }
33
+ if (run_queue) {
34
+ for (let i = 0; i < subscriber_queue.length; i += 2) {
35
+ subscriber_queue[i][0](subscriber_queue[i + 1]);
36
+ }
37
+ subscriber_queue.length = 0;
38
+ }
39
+ }
40
+ }
41
+ }
42
+ function update(fn) {
43
+ set(fn(
44
+ /** @type {T} */
45
+ value
46
+ ));
47
+ }
48
+ function subscribe(run, invalidate = noop) {
49
+ const subscriber = [run, invalidate];
50
+ subscribers.add(subscriber);
51
+ if (subscribers.size === 1) {
52
+ stop = start(set, update) || noop;
53
+ }
54
+ run(
55
+ /** @type {T} */
56
+ value
57
+ );
58
+ return () => {
59
+ subscribers.delete(subscriber);
60
+ if (subscribers.size === 0 && stop) {
61
+ stop();
62
+ stop = null;
63
+ }
64
+ };
65
+ }
66
+ return { set, update, subscribe };
67
+ }
68
+ function get(store2) {
69
+ let value;
70
+ subscribe_to_store(store2, (_) => value = _)();
71
+ return value;
72
+ }
73
+ let untracking = false;
74
+ function untrack(fn) {
75
+ var previous_untracking = untracking;
76
+ try {
77
+ untracking = true;
78
+ return fn();
79
+ } finally {
80
+ untracking = previous_untracking;
81
+ }
82
+ }
83
+ function store(defaultValue) {
84
+ const scope = {
85
+ get: (name) => {
86
+ return get(scope.store)[name];
87
+ },
88
+ set: (name, value) => {
89
+ if (typeof name === "string") {
90
+ Object.assign(get(scope.store), {
91
+ [name]: value
92
+ });
93
+ } else {
94
+ Object.assign(get(scope.store), name);
95
+ }
96
+ scope.store.set(get(scope.store));
97
+ },
98
+ store: writable(defaultValue)
99
+ };
100
+ return scope;
101
+ }
102
+ globalThis.$altcha = globalThis.$altcha || {
103
+ algorithms: /* @__PURE__ */ new Map(),
104
+ defaults: store({}),
105
+ i18n: store({}),
106
+ instances: /* @__PURE__ */ new Set(),
107
+ plugins: /* @__PURE__ */ new Set()
108
+ };
1
109
  class BasePlugin {
2
110
  constructor(host) {
3
111
  this.host = host;
@@ -14,6 +122,42 @@ class BasePlugin {
14
122
  async onVerify(value) {
15
123
  }
16
124
  }
125
+ function getDigest(algorithm) {
126
+ switch (algorithm) {
127
+ case "PBKDF2/SHA-512":
128
+ return "SHA-512";
129
+ case "PBKDF2/SHA-384":
130
+ return "SHA-384";
131
+ case "PBKDF2/SHA-256":
132
+ default:
133
+ return "SHA-256";
134
+ }
135
+ }
136
+ async function deriveKey(parameters, salt, password) {
137
+ const { algorithm, cost, keyLength = 32 } = parameters;
138
+ const passwordKey = await crypto.subtle.importKey(
139
+ "raw",
140
+ password,
141
+ { name: "PBKDF2" },
142
+ false,
143
+ ["deriveKey"]
144
+ );
145
+ const derivedKey = await crypto.subtle.deriveKey(
146
+ {
147
+ name: "PBKDF2",
148
+ salt,
149
+ iterations: cost,
150
+ hash: getDigest(algorithm)
151
+ },
152
+ passwordKey,
153
+ { name: "AES-GCM", length: keyLength * 8 },
154
+ true,
155
+ ["encrypt"]
156
+ );
157
+ return {
158
+ derivedKey: new Uint8Array(await crypto.subtle.exportKey("raw", derivedKey))
159
+ };
160
+ }
17
161
  function bufferStartsWith(buffer, prefix) {
18
162
  if (prefix.length > buffer.length) {
19
163
  return false;
@@ -50,9 +194,45 @@ function hexToBuffer(hex) {
50
194
  async function delay(ms) {
51
195
  await new Promise((resolve) => setTimeout(resolve, ms));
52
196
  }
197
+ async function hmac(algorithm, data, keyStr) {
198
+ const key = await crypto.subtle.importKey(
199
+ "raw",
200
+ new TextEncoder().encode(keyStr),
201
+ {
202
+ name: "HMAC",
203
+ hash: { name: algorithm }
204
+ },
205
+ false,
206
+ ["sign", "verify"]
207
+ );
208
+ const signature = await crypto.subtle.sign(
209
+ "HMAC",
210
+ key,
211
+ typeof data === "string" ? new TextEncoder().encode(data) : data
212
+ );
213
+ return new Uint8Array(signature);
214
+ }
215
+ function sortKeys(obj) {
216
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
217
+ return obj;
218
+ }
219
+ return Object.keys(obj).sort().reduce((acc, key) => {
220
+ const value = obj[key];
221
+ if (value !== void 0) {
222
+ acc[key] = sortKeys(value);
223
+ }
224
+ return acc;
225
+ }, {});
226
+ }
53
227
  function timeDuration(start) {
54
228
  return Math.floor((performance.now() - start) * 10) / 10;
55
229
  }
230
+ var HmacAlgorithm = /* @__PURE__ */ ((HmacAlgorithm2) => {
231
+ HmacAlgorithm2["SHA_256"] = "SHA-256";
232
+ HmacAlgorithm2["SHA_384"] = "SHA-384";
233
+ HmacAlgorithm2["SHA_512"] = "SHA-512";
234
+ return HmacAlgorithm2;
235
+ })(HmacAlgorithm || {});
56
236
  var State = /* @__PURE__ */ ((State2) => {
57
237
  State2["CODE"] = "code";
58
238
  State2["ERROR"] = "error";
@@ -87,6 +267,62 @@ class PasswordBuffer {
87
267
  return this.buffer;
88
268
  }
89
269
  }
270
+ async function createChallenge(options) {
271
+ const {
272
+ algorithm,
273
+ counter,
274
+ counterMode = "uint32",
275
+ cost,
276
+ deriveKey: deriveKey2,
277
+ data,
278
+ expiresAt,
279
+ hmacAlgorithm = HmacAlgorithm.SHA_256,
280
+ hmacKeySignatureSecret,
281
+ hmacSignatureSecret,
282
+ keyLength = 32,
283
+ keyPrefix = "00",
284
+ keyPrefixLength = keyLength / 2,
285
+ memoryCost,
286
+ parallelism
287
+ } = options;
288
+ const parameters = {
289
+ algorithm,
290
+ nonce: bufferToHex(crypto.getRandomValues(new Uint8Array(16))),
291
+ salt: bufferToHex(crypto.getRandomValues(new Uint8Array(16))),
292
+ cost,
293
+ keyLength,
294
+ memoryCost,
295
+ parallelism,
296
+ keyPrefix,
297
+ expiresAt: expiresAt instanceof Date ? Math.floor(expiresAt.getTime() / 1e3) : expiresAt,
298
+ data
299
+ };
300
+ let deriveKeyResult = null;
301
+ if (counter !== void 0) {
302
+ const nonceBuf = hexToBuffer(parameters.nonce);
303
+ deriveKeyResult = await deriveKey2(
304
+ parameters,
305
+ hexToBuffer(parameters.salt),
306
+ new PasswordBuffer(nonceBuf, counterMode).setCounter(counter)
307
+ );
308
+ if (deriveKeyResult.parameters) {
309
+ Object.assign(parameters, deriveKeyResult.parameters);
310
+ }
311
+ parameters.keyPrefix = bufferToHex(deriveKeyResult.derivedKey.slice(0, keyPrefixLength));
312
+ }
313
+ if (!hmacSignatureSecret) {
314
+ return {
315
+ parameters: sortKeys(parameters)
316
+ };
317
+ }
318
+ return signChallenge(
319
+ hmacAlgorithm,
320
+ parameters,
321
+ deriveKeyResult?.derivedKey,
322
+ hmacSignatureSecret,
323
+ hmacKeySignatureSecret
324
+ );
325
+ }
90
326
  async function solveChallenge(options) {
91
327
  const {
92
328
  challenge,
@@ -94,7 +330,7 @@ async function solveChallenge(options) {
94
330
  counterMode = "uint32",
95
331
  counterStart = 0,
96
332
  counterStep = 1,
97
- deriveKey,
333
+ deriveKey: deriveKey2,
98
334
  timeout = 9e4
99
335
  } = options;
100
336
  const { nonce, keyPrefix, salt } = challenge.parameters;
@@ -110,7 +346,7 @@ async function solveChallenge(options) {
110
346
  if (controller?.signal.aborted || timeout && counter % 10 === 0 && performance.now() - start > timeout) {
111
347
  return null;
112
348
  }
113
- const { derivedKey } = await deriveKey(
349
+ const { derivedKey } = await deriveKey2(
114
350
  challenge.parameters,
115
351
  saltBuf,
116
352
  password.setCounter(counter)
@@ -138,7 +374,8 @@ async function solveChallengeWorkers(options) {
138
374
  controller = new AbortController(),
139
375
  createWorker,
140
376
  onOutOfMemory = (c) => c > 1 ? Math.floor(c / 2) : 0,
141
- counterMode
377
+ counterMode,
378
+ timeout = 9e4
142
379
  } = options;
143
380
  const workersConcurrency = Math.min(16, Math.max(1, concurrency));
144
381
  const workersInstances = [];
@@ -179,6 +416,7 @@ async function solveChallengeWorkers(options) {
179
416
  counterMode,
180
417
  counterStart: i,
181
418
  counterStep: workersConcurrency,
419
+ timeout,
182
420
  type: "work"
183
421
  });
184
422
  });
@@ -210,8 +448,24 @@ async function solveChallengeWorkers(options) {
210
448
  }
211
449
  return solution || null;
212
450
  }
451
+ async function signChallenge(algorithm, parameters, derivedKey, hmacSignatureSecret, hmacKeySignatureSecret) {
452
+ if (derivedKey && hmacKeySignatureSecret) {
453
+ parameters.keySignature = bufferToHex(
454
+ await hmac(algorithm, derivedKey, hmacKeySignatureSecret)
455
+ );
456
+ }
457
+ parameters = sortKeys(parameters);
458
+ return {
459
+ parameters,
460
+ signature: bufferToHex(await hmac(algorithm, JSON.stringify(parameters), hmacSignatureSecret))
461
+ };
462
+ }
213
463
  async function deobfuscate(obfuscatedData, options = {}) {
214
- const { concurrency = navigator.hardwareConcurrency, deriveKey: deriveKey2 } = options;
464
+ let {
465
+ concurrency = Math.max(1, Math.min(4, navigator.hardwareConcurrency)),
466
+ createWorker,
467
+ deriveKey: deriveKey$1 = deriveKey
468
+ } = options;
215
469
  let challenge = null;
216
470
  try {
217
471
  challenge = JSON.parse(atob(obfuscatedData));
@@ -223,21 +477,20 @@ async function deobfuscate(obfuscatedData, options = {}) {
223
477
  }
224
478
  const cipher = challenge.cipher;
225
479
  let solution = null;
226
- if (deriveKey2) {
227
- solution = await solveChallenge({
228
- challenge,
229
- deriveKey: deriveKey2
230
- });
231
- } else {
232
- const createWorker = globalThis.$altcha.algorithms.get(challenge.parameters.algorithm);
233
- if (!createWorker) {
234
- throw new Error(`Unsupported algorithm ${challenge.parameters.algorithm}.`);
235
- }
480
+ if (!createWorker && "$altcha" in globalThis) {
481
+ createWorker = globalThis.$altcha.algorithms.get(challenge.parameters.algorithm);
482
+ }
483
+ if (createWorker) {
236
484
  solution = await solveChallengeWorkers({
237
485
  challenge,
238
486
  concurrency,
239
487
  createWorker
240
488
  });
489
+ } else {
490
+ solution = await solveChallenge({
491
+ challenge,
492
+ deriveKey: deriveKey$1
493
+ });
241
494
  }
242
495
  if (!solution) {
243
496
  throw new Error("Unable to find solution.");
@@ -259,7 +512,48 @@ async function deobfuscate(obfuscatedData, options = {}) {
259
512
  );
260
513
  return new TextDecoder().decode(result);
261
514
  }
515
+ async function obfuscate(str, options = {}) {
516
+ const { deriveKey: deriveKey$1 = deriveKey } = options;
517
+ const counterMin = options?.counterMin || 20;
518
+ const counterMax = options?.counterMax || 200;
519
+ const { parameters } = await createChallenge({
520
+ algorithm: "PBKDF2/SHA-256",
521
+ cost: 5e3,
522
+ deriveKey: deriveKey$1,
523
+ counter: Math.floor(Math.random() * (counterMax - counterMin + 1)) + counterMin,
524
+ keyPrefixLength: 32,
525
+ ...options
526
+ });
527
+ const key = await crypto.subtle.importKey(
528
+ "raw",
529
+ hexToBuffer(parameters.keyPrefix),
530
+ { name: "AES-GCM" },
531
+ false,
532
+ ["encrypt"]
533
+ );
534
+ const iv = crypto.getRandomValues(new Uint8Array(12));
535
+ const data = await crypto.subtle.encrypt(
536
+ { name: "AES-GCM", iv },
537
+ key,
538
+ new TextEncoder().encode(str)
539
+ );
540
+ return btoa(
541
+ JSON.stringify({
542
+ parameters: {
543
+ ...parameters,
544
+ // Return only half the derived key
545
+ keyPrefix: parameters.keyPrefix.slice(0, parameters.keyLength || 32)
546
+ },
547
+ cipher: {
548
+ iv: bufferToHex(iv),
549
+ data: bufferToHex(data)
550
+ }
551
+ })
552
+ );
553
+ }
262
554
  class ObfuscationPlugin extends BasePlugin {
555
+ static deobfuscate = deobfuscate;
556
+ static obfuscate = obfuscate;
263
557
  elTrigger = null;
264
558
  activate() {
265
559
  this.elTrigger = this.host.querySelector("button");
@@ -1 +1 @@
1
- class e{constructor(e){this.host=e}static register(e){"$altcha"in globalThis&&!globalThis.$altcha.plugins.has(e)&&globalThis.$altcha.plugins.add(e)}async onFetchChallenge(e){}async onRequestServerVerification(e,t){}async onVerify(e){}}function t(e,t){if(t.length>e.length)return!1;for(let r=0;r<t.length;r++)if(e[r]!==t[r])return!1;return!0}function r(e){return Array.from(new Uint8Array(e)).map(e=>e.toString(16).padStart(2,"0")).join("")}function n(e){if(e.length%2!=0)throw new Error(`Hex string must have an even length. Got: ${e}`);const t=new ArrayBuffer(e.length/2),r=new DataView(t);for(let t=0;t<e.length;t+=2){const n=e.substring(t,t+2),a=parseInt(n,16);r.setUint8(t/2,a)}return new Uint8Array(t)}async function a(e){await new Promise(t=>setTimeout(t,e))}function o(e){return Math.floor(10*(performance.now()-e))/10}var i=(e=>(e.CODE="code",e.ERROR="error",e.VERIFIED="verified",e.VERIFYING="verifying",e.UNVERIFIED="unverified",e.EXPIRED="expired",e))(i||{});class s{constructor(e,t="uint32"){this.nonce=e,this.mode=t,this.buffer=new Uint8Array(this.nonce.length+this.COUNTER_BYTES),this.buffer.set(this.nonce,0),this.dataView=new DataView(this.buffer.buffer)}COUNTER_BYTES=4;buffer;dataView;encoder=new TextEncoder;setCounter(e){return"string"===this.mode?function(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e,0),r.set(t,e.length),r}(this.nonce,this.encoder.encode(e.toString())):(this.dataView.setUint32(this.nonce.length,e,!1),this.buffer)}}async function c(e){const{challenge:t,concurrency:r=navigator.hardwareConcurrency,controller:n=new AbortController,createWorker:a,onOutOfMemory:o=e=>e>1?Math.floor(e/2):0,counterMode:i}=e,s=Math.min(16,Math.max(1,r)),l=[],h=()=>{for(const e of l)e.terminate()};for(let e=0;e<s;e++)l.push(await a(t.parameters.algorithm));let u=null;try{u=await Promise.race(l.map((e,r)=>(n.signal.addEventListener("abort",()=>{e.postMessage({type:"abort"})}),new Promise((n,a)=>{e.addEventListener("error",e=>{a(e)}),e.addEventListener("message",t=>{if(t.data){for(const t of l)t!==e&&t.postMessage({type:"abort"});if(t.data.error)return a(new Error(t.data.error))}n(t.data)}),e.postMessage({challenge:t,counterMode:i,counterStart:r,counterStep:s,type:"work"})}))))}catch(r){if(r instanceof Error&&!!r?.message?.includes("Out of memory")&&o){h();const r=o(s);if(r)return c({...e,challenge:t,controller:n,concurrency:r,createWorker:a})}throw r}finally{h()}return n.signal.aborted?null:u||null}async function l(e,i={}){const{concurrency:l=navigator.hardwareConcurrency,deriveKey:h}=i;let u=null;try{u=JSON.parse(atob(e))}catch{throw new Error("Unable to parse obfuscated data.")}if(!u||"object"!=typeof u||!("parameters"in u)||!("cipher"in u))throw new Error("Invalid obfuscated data format.");const f=u.cipher;let g=null;if(h)g=await async function(e){const{challenge:i,controller:c,counterMode:l="uint32",counterStart:h=0,counterStep:u=1,deriveKey:f,timeout:g=9e4}=e,{nonce:d,keyPrefix:w,salt:m}=i.parameters,y=n(d),p=n(m),E=w.length%2==0?n(w):null,b=new s(y,l),T=performance.now();let v=h,S="",C=T;for(;;){if(c?.signal.aborted||g&&v%10==0&&performance.now()-T>g)return null;const{derivedKey:e}=await f(i.parameters,p,b.setCounter(v));if(v%10==0&&performance.now()-C>200&&(await a(0),C=performance.now()),E?t(e,E):r(e).startsWith(w)){S=r(e);break}v+=u}return{counter:v,derivedKey:S,time:o(T)}}({challenge:u,deriveKey:h});else{const e=globalThis.$altcha.algorithms.get(u.parameters.algorithm);if(!e)throw new Error(`Unsupported algorithm ${u.parameters.algorithm}.`);g=await c({challenge:u,concurrency:l,createWorker:e})}if(!g)throw new Error("Unable to find solution.");const d=await crypto.subtle.importKey("raw",n(g.derivedKey),{name:"AES-GCM"},!1,["decrypt"]),w=await crypto.subtle.decrypt({name:"AES-GCM",iv:n(f.iv)},d,n(f.data));return(new TextDecoder).decode(w)}e.register(class extends e{elTrigger=null;activate(){this.elTrigger=this.host.querySelector("button"),this.elTrigger&&(this.elTrigger.addEventListener("click",this.onTriggerClick.bind(this)),this.host.configure({floatingAnchor:this.elTrigger}))}destroy(){}async onVerify(e){const{minDuration:t=500}=e,r=performance.now(),n=this.host.getAttribute("data-obfuscated");if(n){this.host.reset(i.VERIFYING);try{const e=await l(n);await this.#e(Math.max(0,t-(performance.now()-r))),this.#t(e)}catch(e){this.host.setState(i.ERROR,String(e))}finally{this.host.setState(i.VERIFIED)}return null}}onTriggerClick(e){e.preventDefault(),this.host.show(),this.host.verify().then(()=>{this.host.hide()})}#t(e){let t;if(e.match(/^(mailto|tel|sms|https?):/)){const[r]=e.slice(e.indexOf(":")+1).replace(/^\/\//,"").split("?");t=document.createElement("a"),t.href=e,t.innerText=r}else t=document.createTextNode(e);this.elTrigger&&t&&(this.elTrigger.after(t),this.elTrigger.parentElement?.removeChild(this.elTrigger))}async#e(e){await new Promise(t=>setTimeout(t,e))}});
1
+ const e=()=>{};function t(t,r,n){if(null==t)return r(void 0),e;const a=function(e){var t=o;try{return o=!0,e()}finally{o=t}}(()=>t.subscribe(r,n));return a.unsubscribe?()=>a.unsubscribe():a}const r=[];function n(t,n=e){let a=null;const o=new Set;function i(e){if(i=e,((n=t)!=n?i==i:n!==i||null!==n&&"object"==typeof n||"function"==typeof n)&&(t=e,a)){const e=!r.length;for(const e of o)e[1](),r.push(e,t);if(e){for(let e=0;e<r.length;e+=2)r[e][0](r[e+1]);r.length=0}}var n,i}function s(e){i(e(t))}return{set:i,update:s,subscribe:function(r,c=e){const l=[r,c];return o.add(l),1===o.size&&(a=n(i,s)||e),r(t),()=>{o.delete(l),0===o.size&&a&&(a(),a=null)}}}}function a(e){let r;return t(e,e=>r=e)(),r}let o=!1;function i(e){const t={get:e=>a(t.store)[e],set:(e,r)=>{"string"==typeof e?Object.assign(a(t.store),{[e]:r}):Object.assign(a(t.store),e),t.store.set(a(t.store))},store:n(e)};return t}globalThis.$altcha=globalThis.$altcha||{algorithms:new Map,defaults:i({}),i18n:i({}),instances:new Set,plugins:new Set};class s{constructor(e){this.host=e}static register(e){"$altcha"in globalThis&&!globalThis.$altcha.plugins.has(e)&&globalThis.$altcha.plugins.add(e)}async onFetchChallenge(e){}async onRequestServerVerification(e,t){}async onVerify(e){}}function c(e){switch(e){case"PBKDF2/SHA-512":return"SHA-512";case"PBKDF2/SHA-384":return"SHA-384";default:return"SHA-256"}}async function l(e,t,r){const{algorithm:n,cost:a,keyLength:o=32}=e,i=await crypto.subtle.importKey("raw",r,{name:"PBKDF2"},!1,["deriveKey"]),s=await crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:a,hash:c(n)},i,{name:"AES-GCM",length:8*o},!0,["encrypt"]);return{derivedKey:new Uint8Array(await crypto.subtle.exportKey("raw",s))}}function u(e,t){if(t.length>e.length)return!1;for(let r=0;r<t.length;r++)if(e[r]!==t[r])return!1;return!0}function h(e){return Array.from(new Uint8Array(e)).map(e=>e.toString(16).padStart(2,"0")).join("")}function f(e){if(e.length%2!=0)throw new Error(`Hex string must have an even length. Got: ${e}`);const t=new ArrayBuffer(e.length/2),r=new DataView(t);for(let t=0;t<e.length;t+=2){const n=e.substring(t,t+2),a=parseInt(n,16);r.setUint8(t/2,a)}return new Uint8Array(t)}async function y(e){await new Promise(t=>setTimeout(t,e))}async function g(e,t,r){const n=await crypto.subtle.importKey("raw",(new TextEncoder).encode(r),{name:"HMAC",hash:{name:e}},!1,["sign","verify"]),a=await crypto.subtle.sign("HMAC",n,"string"==typeof t?(new TextEncoder).encode(t):t);return new Uint8Array(a)}function d(e){return"object"!=typeof e||null===e||Array.isArray(e)?e:Object.keys(e).sort().reduce((t,r)=>{const n=e[r];return void 0!==n&&(t[r]=d(n)),t},{})}function m(e){return Math.floor(10*(performance.now()-e))/10}var w=(e=>(e.SHA_256="SHA-256",e.SHA_384="SHA-384",e.SHA_512="SHA-512",e))(w||{}),p=(e=>(e.CODE="code",e.ERROR="error",e.VERIFIED="verified",e.VERIFYING="verifying",e.UNVERIFIED="unverified",e.EXPIRED="expired",e))(p||{});class b{constructor(e,t="uint32"){this.nonce=e,this.mode=t,this.buffer=new Uint8Array(this.nonce.length+this.COUNTER_BYTES),this.buffer.set(this.nonce,0),this.dataView=new DataView(this.buffer.buffer)}COUNTER_BYTES=4;buffer;dataView;encoder=new TextEncoder;setCounter(e){return"string"===this.mode?function(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e,0),r.set(t,e.length),r}(this.nonce,this.encoder.encode(e.toString())):(this.dataView.setUint32(this.nonce.length,e,!1),this.buffer)}}async function S(e){const{algorithm:t,counter:r,counterMode:n="uint32",cost:a,deriveKey:o,data:i,expiresAt:s,hmacAlgorithm:c=w.SHA_256,hmacKeySignatureSecret:l,hmacSignatureSecret:u,keyLength:y=32,keyPrefix:m="00",keyPrefixLength:p=y/2,memoryCost:S,parallelism:v}=e,A={algorithm:t,nonce:h(crypto.getRandomValues(new Uint8Array(16))),salt:h(crypto.getRandomValues(new Uint8Array(16))),cost:a,keyLength:y,memoryCost:S,parallelism:v,keyPrefix:m,expiresAt:s instanceof Date?Math.floor(s.getTime()/1e3):s,data:i};let E=null;if(void 0!==r){const e=f(A.nonce);E=await o(A,f(A.salt),new b(e,n).setCounter(r)),E.parameters&&Object.assign(A,E.parameters),A.keyPrefix=h(E.derivedKey.slice(0,p))}return u?async function(e,t,r,n,a){r&&a&&(t.keySignature=h(await g(e,r,a)));return t=d(t),{parameters:t,signature:h(await g(e,JSON.stringify(t),n))}}(c,A,E?.derivedKey,u,l):{parameters:d(A)}}async function v(e){const{challenge:t,concurrency:r=navigator.hardwareConcurrency,controller:n=new AbortController,createWorker:a,onOutOfMemory:o=e=>e>1?Math.floor(e/2):0,counterMode:i,timeout:s=9e4}=e,c=Math.min(16,Math.max(1,r)),l=[],u=()=>{for(const e of l)e.terminate()};for(let e=0;e<c;e++)l.push(await a(t.parameters.algorithm));let h=null;try{h=await Promise.race(l.map((e,r)=>(n.signal.addEventListener("abort",()=>{e.postMessage({type:"abort"})}),new Promise((n,a)=>{e.addEventListener("error",e=>{a(e)}),e.addEventListener("message",t=>{if(t.data){for(const t of l)t!==e&&t.postMessage({type:"abort"});if(t.data.error)return a(new Error(t.data.error))}n(t.data)}),e.postMessage({challenge:t,counterMode:i,counterStart:r,counterStep:c,timeout:s,type:"work"})}))))}catch(r){if(r instanceof Error&&!!r?.message?.includes("Out of memory")&&o){u();const r=o(c);if(r)return v({...e,challenge:t,controller:n,concurrency:r,createWorker:a})}throw r}finally{u()}return n.signal.aborted?null:h||null}async function A(e,t={}){let{concurrency:r=Math.max(1,Math.min(4,navigator.hardwareConcurrency)),createWorker:n,deriveKey:a=l}=t,o=null;try{o=JSON.parse(atob(e))}catch{throw new Error("Unable to parse obfuscated data.")}if(!o||"object"!=typeof o||!("parameters"in o)||!("cipher"in o))throw new Error("Invalid obfuscated data format.");const i=o.cipher;let s=null;if(!n&&"$altcha"in globalThis&&(n=globalThis.$altcha.algorithms.get(o.parameters.algorithm)),s=n?await v({challenge:o,concurrency:r,createWorker:n}):await async function(e){const{challenge:t,controller:r,counterMode:n="uint32",counterStart:a=0,counterStep:o=1,deriveKey:i,timeout:s=9e4}=e,{nonce:c,keyPrefix:l,salt:g}=t.parameters,d=f(c),w=f(g),p=l.length%2==0?f(l):null,S=new b(d,n),v=performance.now();let A=a,E="",T=v;for(;;){if(r?.signal.aborted||s&&A%10==0&&performance.now()-v>s)return null;const{derivedKey:e}=await i(t.parameters,w,S.setCounter(A));if(A%10==0&&performance.now()-T>200&&(await y(0),T=performance.now()),p?u(e,p):h(e).startsWith(l)){E=h(e);break}A+=o}return{counter:A,derivedKey:E,time:m(v)}}({challenge:o,deriveKey:a}),!s)throw new Error("Unable to find solution.");const c=await crypto.subtle.importKey("raw",f(s.derivedKey),{name:"AES-GCM"},!1,["decrypt"]),g=await crypto.subtle.decrypt({name:"AES-GCM",iv:f(i.iv)},c,f(i.data));return(new TextDecoder).decode(g)}async function E(e,t={}){const{deriveKey:r=l}=t,n=t?.counterMin||20,a=t?.counterMax||200,{parameters:o}=await S({algorithm:"PBKDF2/SHA-256",cost:5e3,deriveKey:r,counter:Math.floor(Math.random()*(a-n+1))+n,keyPrefixLength:32,...t}),i=await crypto.subtle.importKey("raw",f(o.keyPrefix),{name:"AES-GCM"},!1,["encrypt"]),s=crypto.getRandomValues(new Uint8Array(12)),c=await crypto.subtle.encrypt({name:"AES-GCM",iv:s},i,(new TextEncoder).encode(e));return btoa(JSON.stringify({parameters:{...o,keyPrefix:o.keyPrefix.slice(0,o.keyLength||32)},cipher:{iv:h(s),data:h(c)}}))}s.register(class extends s{static deobfuscate=A;static obfuscate=E;elTrigger=null;activate(){this.elTrigger=this.host.querySelector("button"),this.elTrigger&&(this.elTrigger.addEventListener("click",this.onTriggerClick.bind(this)),this.host.configure({floatingAnchor:this.elTrigger}))}destroy(){}async onVerify(e){const{minDuration:t=500}=e,r=performance.now(),n=this.host.getAttribute("data-obfuscated");if(n){this.host.reset(p.VERIFYING);try{const e=await A(n);await this.#e(Math.max(0,t-(performance.now()-r))),this.#t(e)}catch(e){this.host.setState(p.ERROR,String(e))}finally{this.host.setState(p.VERIFIED)}return null}}onTriggerClick(e){e.preventDefault(),this.host.show(),this.host.verify().then(()=>{this.host.hide()})}#t(e){let t;if(e.match(/^(mailto|tel|sms|https?):/)){const[r]=e.slice(e.indexOf(":")+1).replace(/^\/\//,"").split("?");t=document.createElement("a"),t.href=e,t.innerText=r}else t=document.createTextNode(e);this.elTrigger&&t&&(this.elTrigger.after(t),this.elTrigger.parentElement?.removeChild(this.elTrigger))}async#e(e){await new Promise(t=>setTimeout(t,e))}});