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.
- package/README.md +5 -2
- package/dist/external/altcha.js +247 -36
- package/dist/external/altcha.min.js +1 -1
- package/dist/external/altcha.umd.cjs +247 -36
- package/dist/external/altcha.umd.min.cjs +1 -1
- package/dist/lib/index.d.ts +18 -6
- package/dist/lib/index.js +3 -1
- package/dist/lib/index.min.js +1 -1
- package/dist/lib/index.umd.cjs +3 -1
- package/dist/lib/index.umd.min.cjs +1 -1
- package/dist/main/altcha.i18n.js +253 -40
- package/dist/main/altcha.i18n.min.js +1 -1
- package/dist/main/altcha.i18n.umd.cjs +253 -40
- package/dist/main/altcha.i18n.umd.min.cjs +1 -1
- package/dist/main/altcha.js +253 -40
- package/dist/main/altcha.min.js +1 -1
- package/dist/main/altcha.umd.cjs +253 -40
- package/dist/main/altcha.umd.min.cjs +1 -1
- package/dist/plugins/obfuscation.plugin.js +308 -14
- package/dist/plugins/obfuscation.plugin.min.js +1 -1
- package/dist/plugins/obfuscation.plugin.umd.cjs +308 -14
- package/dist/plugins/obfuscation.plugin.umd.min.cjs +1 -1
- package/dist/types/generic.d.ts +4 -1
- package/dist/types/index.d.ts +18 -6
- package/dist/workers/argon2id.js +3 -2
- package/dist/workers/pbkdf2.js +3 -2
- package/dist/workers/scrypt.js +3 -2
- package/dist/workers/sha.js +3 -2
- package/package.json +2 -2
- package/dist/external/index.d.ts +0 -1
|
@@ -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
|
|
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
|
-
|
|
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 (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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))}});
|