altcha 3.0.0-beta.3 → 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/dist/external/altcha.js +1 -1
- package/dist/external/altcha.min.js +1 -1
- package/dist/external/altcha.umd.cjs +1 -1
- package/dist/external/altcha.umd.min.cjs +1 -1
- package/dist/main/altcha.i18n.js +1 -1
- package/dist/main/altcha.i18n.min.js +1 -1
- package/dist/main/altcha.i18n.umd.cjs +1 -1
- package/dist/main/altcha.i18n.umd.min.cjs +1 -1
- package/dist/main/altcha.js +1 -1
- package/dist/main/altcha.min.js +1 -1
- package/dist/main/altcha.umd.cjs +1 -1
- package/dist/main/altcha.umd.min.cjs +1 -1
- package/dist/plugins/obfuscation.plugin.js +253 -0
- package/dist/plugins/obfuscation.plugin.min.js +1 -1
- package/dist/plugins/obfuscation.plugin.umd.cjs +253 -0
- package/dist/plugins/obfuscation.plugin.umd.min.cjs +1 -1
- package/package.json +1 -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;
|
|
@@ -86,9 +194,45 @@ function hexToBuffer(hex) {
|
|
|
86
194
|
async function delay(ms) {
|
|
87
195
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
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
|
+
}
|
|
89
227
|
function timeDuration(start) {
|
|
90
228
|
return Math.floor((performance.now() - start) * 10) / 10;
|
|
91
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 || {});
|
|
92
236
|
var State = /* @__PURE__ */ ((State2) => {
|
|
93
237
|
State2["CODE"] = "code";
|
|
94
238
|
State2["ERROR"] = "error";
|
|
@@ -123,6 +267,62 @@ class PasswordBuffer {
|
|
|
123
267
|
return this.buffer;
|
|
124
268
|
}
|
|
125
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
|
+
}
|
|
126
326
|
async function solveChallenge(options) {
|
|
127
327
|
const {
|
|
128
328
|
challenge,
|
|
@@ -248,6 +448,18 @@ async function solveChallengeWorkers(options) {
|
|
|
248
448
|
}
|
|
249
449
|
return solution || null;
|
|
250
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
|
+
}
|
|
251
463
|
async function deobfuscate(obfuscatedData, options = {}) {
|
|
252
464
|
let {
|
|
253
465
|
concurrency = Math.max(1, Math.min(4, navigator.hardwareConcurrency)),
|
|
@@ -300,7 +512,48 @@ async function deobfuscate(obfuscatedData, options = {}) {
|
|
|
300
512
|
);
|
|
301
513
|
return new TextDecoder().decode(result);
|
|
302
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
|
+
}
|
|
303
554
|
class ObfuscationPlugin extends BasePlugin {
|
|
555
|
+
static deobfuscate = deobfuscate;
|
|
556
|
+
static obfuscate = obfuscate;
|
|
304
557
|
elTrigger = null;
|
|
305
558
|
activate() {
|
|
306
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))}});
|
|
@@ -2,6 +2,114 @@
|
|
|
2
2
|
typeof define === "function" && define.amd ? define(factory) : factory();
|
|
3
3
|
})((function() {
|
|
4
4
|
"use strict";
|
|
5
|
+
const noop = () => {
|
|
6
|
+
};
|
|
7
|
+
function safe_not_equal(a, b) {
|
|
8
|
+
return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function";
|
|
9
|
+
}
|
|
10
|
+
function subscribe_to_store(store2, run, invalidate) {
|
|
11
|
+
if (store2 == null) {
|
|
12
|
+
run(void 0);
|
|
13
|
+
return noop;
|
|
14
|
+
}
|
|
15
|
+
const unsub = untrack(
|
|
16
|
+
() => store2.subscribe(
|
|
17
|
+
run,
|
|
18
|
+
// @ts-expect-error
|
|
19
|
+
invalidate
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
|
|
23
|
+
}
|
|
24
|
+
const subscriber_queue = [];
|
|
25
|
+
function writable(value, start = noop) {
|
|
26
|
+
let stop = null;
|
|
27
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
28
|
+
function set(new_value) {
|
|
29
|
+
if (safe_not_equal(value, new_value)) {
|
|
30
|
+
value = new_value;
|
|
31
|
+
if (stop) {
|
|
32
|
+
const run_queue = !subscriber_queue.length;
|
|
33
|
+
for (const subscriber of subscribers) {
|
|
34
|
+
subscriber[1]();
|
|
35
|
+
subscriber_queue.push(subscriber, value);
|
|
36
|
+
}
|
|
37
|
+
if (run_queue) {
|
|
38
|
+
for (let i = 0; i < subscriber_queue.length; i += 2) {
|
|
39
|
+
subscriber_queue[i][0](subscriber_queue[i + 1]);
|
|
40
|
+
}
|
|
41
|
+
subscriber_queue.length = 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function update(fn) {
|
|
47
|
+
set(fn(
|
|
48
|
+
/** @type {T} */
|
|
49
|
+
value
|
|
50
|
+
));
|
|
51
|
+
}
|
|
52
|
+
function subscribe(run, invalidate = noop) {
|
|
53
|
+
const subscriber = [run, invalidate];
|
|
54
|
+
subscribers.add(subscriber);
|
|
55
|
+
if (subscribers.size === 1) {
|
|
56
|
+
stop = start(set, update) || noop;
|
|
57
|
+
}
|
|
58
|
+
run(
|
|
59
|
+
/** @type {T} */
|
|
60
|
+
value
|
|
61
|
+
);
|
|
62
|
+
return () => {
|
|
63
|
+
subscribers.delete(subscriber);
|
|
64
|
+
if (subscribers.size === 0 && stop) {
|
|
65
|
+
stop();
|
|
66
|
+
stop = null;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { set, update, subscribe };
|
|
71
|
+
}
|
|
72
|
+
function get(store2) {
|
|
73
|
+
let value;
|
|
74
|
+
subscribe_to_store(store2, (_) => value = _)();
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
let untracking = false;
|
|
78
|
+
function untrack(fn) {
|
|
79
|
+
var previous_untracking = untracking;
|
|
80
|
+
try {
|
|
81
|
+
untracking = true;
|
|
82
|
+
return fn();
|
|
83
|
+
} finally {
|
|
84
|
+
untracking = previous_untracking;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function store(defaultValue) {
|
|
88
|
+
const scope = {
|
|
89
|
+
get: (name) => {
|
|
90
|
+
return get(scope.store)[name];
|
|
91
|
+
},
|
|
92
|
+
set: (name, value) => {
|
|
93
|
+
if (typeof name === "string") {
|
|
94
|
+
Object.assign(get(scope.store), {
|
|
95
|
+
[name]: value
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
Object.assign(get(scope.store), name);
|
|
99
|
+
}
|
|
100
|
+
scope.store.set(get(scope.store));
|
|
101
|
+
},
|
|
102
|
+
store: writable(defaultValue)
|
|
103
|
+
};
|
|
104
|
+
return scope;
|
|
105
|
+
}
|
|
106
|
+
globalThis.$altcha = globalThis.$altcha || {
|
|
107
|
+
algorithms: /* @__PURE__ */ new Map(),
|
|
108
|
+
defaults: store({}),
|
|
109
|
+
i18n: store({}),
|
|
110
|
+
instances: /* @__PURE__ */ new Set(),
|
|
111
|
+
plugins: /* @__PURE__ */ new Set()
|
|
112
|
+
};
|
|
5
113
|
class BasePlugin {
|
|
6
114
|
constructor(host) {
|
|
7
115
|
this.host = host;
|
|
@@ -90,9 +198,45 @@
|
|
|
90
198
|
async function delay(ms) {
|
|
91
199
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
92
200
|
}
|
|
201
|
+
async function hmac(algorithm, data, keyStr) {
|
|
202
|
+
const key = await crypto.subtle.importKey(
|
|
203
|
+
"raw",
|
|
204
|
+
new TextEncoder().encode(keyStr),
|
|
205
|
+
{
|
|
206
|
+
name: "HMAC",
|
|
207
|
+
hash: { name: algorithm }
|
|
208
|
+
},
|
|
209
|
+
false,
|
|
210
|
+
["sign", "verify"]
|
|
211
|
+
);
|
|
212
|
+
const signature = await crypto.subtle.sign(
|
|
213
|
+
"HMAC",
|
|
214
|
+
key,
|
|
215
|
+
typeof data === "string" ? new TextEncoder().encode(data) : data
|
|
216
|
+
);
|
|
217
|
+
return new Uint8Array(signature);
|
|
218
|
+
}
|
|
219
|
+
function sortKeys(obj) {
|
|
220
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
221
|
+
return obj;
|
|
222
|
+
}
|
|
223
|
+
return Object.keys(obj).sort().reduce((acc, key) => {
|
|
224
|
+
const value = obj[key];
|
|
225
|
+
if (value !== void 0) {
|
|
226
|
+
acc[key] = sortKeys(value);
|
|
227
|
+
}
|
|
228
|
+
return acc;
|
|
229
|
+
}, {});
|
|
230
|
+
}
|
|
93
231
|
function timeDuration(start) {
|
|
94
232
|
return Math.floor((performance.now() - start) * 10) / 10;
|
|
95
233
|
}
|
|
234
|
+
var HmacAlgorithm = /* @__PURE__ */ ((HmacAlgorithm2) => {
|
|
235
|
+
HmacAlgorithm2["SHA_256"] = "SHA-256";
|
|
236
|
+
HmacAlgorithm2["SHA_384"] = "SHA-384";
|
|
237
|
+
HmacAlgorithm2["SHA_512"] = "SHA-512";
|
|
238
|
+
return HmacAlgorithm2;
|
|
239
|
+
})(HmacAlgorithm || {});
|
|
96
240
|
var State = /* @__PURE__ */ ((State2) => {
|
|
97
241
|
State2["CODE"] = "code";
|
|
98
242
|
State2["ERROR"] = "error";
|
|
@@ -127,6 +271,62 @@
|
|
|
127
271
|
return this.buffer;
|
|
128
272
|
}
|
|
129
273
|
}
|
|
274
|
+
async function createChallenge(options) {
|
|
275
|
+
const {
|
|
276
|
+
algorithm,
|
|
277
|
+
counter,
|
|
278
|
+
counterMode = "uint32",
|
|
279
|
+
cost,
|
|
280
|
+
deriveKey: deriveKey2,
|
|
281
|
+
data,
|
|
282
|
+
expiresAt,
|
|
283
|
+
hmacAlgorithm = HmacAlgorithm.SHA_256,
|
|
284
|
+
hmacKeySignatureSecret,
|
|
285
|
+
hmacSignatureSecret,
|
|
286
|
+
keyLength = 32,
|
|
287
|
+
keyPrefix = "00",
|
|
288
|
+
keyPrefixLength = keyLength / 2,
|
|
289
|
+
memoryCost,
|
|
290
|
+
parallelism
|
|
291
|
+
} = options;
|
|
292
|
+
const parameters = {
|
|
293
|
+
algorithm,
|
|
294
|
+
nonce: bufferToHex(crypto.getRandomValues(new Uint8Array(16))),
|
|
295
|
+
salt: bufferToHex(crypto.getRandomValues(new Uint8Array(16))),
|
|
296
|
+
cost,
|
|
297
|
+
keyLength,
|
|
298
|
+
memoryCost,
|
|
299
|
+
parallelism,
|
|
300
|
+
keyPrefix,
|
|
301
|
+
expiresAt: expiresAt instanceof Date ? Math.floor(expiresAt.getTime() / 1e3) : expiresAt,
|
|
302
|
+
data
|
|
303
|
+
};
|
|
304
|
+
let deriveKeyResult = null;
|
|
305
|
+
if (counter !== void 0) {
|
|
306
|
+
const nonceBuf = hexToBuffer(parameters.nonce);
|
|
307
|
+
deriveKeyResult = await deriveKey2(
|
|
308
|
+
parameters,
|
|
309
|
+
hexToBuffer(parameters.salt),
|
|
310
|
+
new PasswordBuffer(nonceBuf, counterMode).setCounter(counter)
|
|
311
|
+
);
|
|
312
|
+
if (deriveKeyResult.parameters) {
|
|
313
|
+
Object.assign(parameters, deriveKeyResult.parameters);
|
|
314
|
+
}
|
|
315
|
+
parameters.keyPrefix = bufferToHex(deriveKeyResult.derivedKey.slice(0, keyPrefixLength));
|
|
316
|
+
}
|
|
317
|
+
if (!hmacSignatureSecret) {
|
|
318
|
+
return {
|
|
319
|
+
parameters: sortKeys(parameters)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
return signChallenge(
|
|
323
|
+
hmacAlgorithm,
|
|
324
|
+
parameters,
|
|
325
|
+
deriveKeyResult?.derivedKey,
|
|
326
|
+
hmacSignatureSecret,
|
|
327
|
+
hmacKeySignatureSecret
|
|
328
|
+
);
|
|
329
|
+
}
|
|
130
330
|
async function solveChallenge(options) {
|
|
131
331
|
const {
|
|
132
332
|
challenge,
|
|
@@ -252,6 +452,18 @@
|
|
|
252
452
|
}
|
|
253
453
|
return solution || null;
|
|
254
454
|
}
|
|
455
|
+
async function signChallenge(algorithm, parameters, derivedKey, hmacSignatureSecret, hmacKeySignatureSecret) {
|
|
456
|
+
if (derivedKey && hmacKeySignatureSecret) {
|
|
457
|
+
parameters.keySignature = bufferToHex(
|
|
458
|
+
await hmac(algorithm, derivedKey, hmacKeySignatureSecret)
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
parameters = sortKeys(parameters);
|
|
462
|
+
return {
|
|
463
|
+
parameters,
|
|
464
|
+
signature: bufferToHex(await hmac(algorithm, JSON.stringify(parameters), hmacSignatureSecret))
|
|
465
|
+
};
|
|
466
|
+
}
|
|
255
467
|
async function deobfuscate(obfuscatedData, options = {}) {
|
|
256
468
|
let {
|
|
257
469
|
concurrency = Math.max(1, Math.min(4, navigator.hardwareConcurrency)),
|
|
@@ -304,7 +516,48 @@
|
|
|
304
516
|
);
|
|
305
517
|
return new TextDecoder().decode(result);
|
|
306
518
|
}
|
|
519
|
+
async function obfuscate(str, options = {}) {
|
|
520
|
+
const { deriveKey: deriveKey$1 = deriveKey } = options;
|
|
521
|
+
const counterMin = options?.counterMin || 20;
|
|
522
|
+
const counterMax = options?.counterMax || 200;
|
|
523
|
+
const { parameters } = await createChallenge({
|
|
524
|
+
algorithm: "PBKDF2/SHA-256",
|
|
525
|
+
cost: 5e3,
|
|
526
|
+
deriveKey: deriveKey$1,
|
|
527
|
+
counter: Math.floor(Math.random() * (counterMax - counterMin + 1)) + counterMin,
|
|
528
|
+
keyPrefixLength: 32,
|
|
529
|
+
...options
|
|
530
|
+
});
|
|
531
|
+
const key = await crypto.subtle.importKey(
|
|
532
|
+
"raw",
|
|
533
|
+
hexToBuffer(parameters.keyPrefix),
|
|
534
|
+
{ name: "AES-GCM" },
|
|
535
|
+
false,
|
|
536
|
+
["encrypt"]
|
|
537
|
+
);
|
|
538
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
539
|
+
const data = await crypto.subtle.encrypt(
|
|
540
|
+
{ name: "AES-GCM", iv },
|
|
541
|
+
key,
|
|
542
|
+
new TextEncoder().encode(str)
|
|
543
|
+
);
|
|
544
|
+
return btoa(
|
|
545
|
+
JSON.stringify({
|
|
546
|
+
parameters: {
|
|
547
|
+
...parameters,
|
|
548
|
+
// Return only half the derived key
|
|
549
|
+
keyPrefix: parameters.keyPrefix.slice(0, parameters.keyLength || 32)
|
|
550
|
+
},
|
|
551
|
+
cipher: {
|
|
552
|
+
iv: bufferToHex(iv),
|
|
553
|
+
data: bufferToHex(data)
|
|
554
|
+
}
|
|
555
|
+
})
|
|
556
|
+
);
|
|
557
|
+
}
|
|
307
558
|
class ObfuscationPlugin extends BasePlugin {
|
|
559
|
+
static deobfuscate = deobfuscate;
|
|
560
|
+
static obfuscate = obfuscate;
|
|
308
561
|
elTrigger = null;
|
|
309
562
|
activate() {
|
|
310
563
|
this.elTrigger = this.host.querySelector("button");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";
|
|
1
|
+
!function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";const e=()=>{};function t(t,r,n){if(null==t)return r(void 0),e;const a=function(e){var t=i;try{return i=!0,e()}finally{i=t}}(()=>t.subscribe(r,n));return a.unsubscribe?()=>a.unsubscribe():a}const r=[];function n(t,n=e){let a=null;const i=new Set;function o(e){if(o=e,((n=t)!=n?o==o:n!==o||null!==n&&"object"==typeof n||"function"==typeof n)&&(t=e,a)){const e=!r.length;for(const e of i)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,o}function s(e){o(e(t))}return{set:o,update:s,subscribe:function(r,c=e){const l=[r,c];return i.add(l),1===i.size&&(a=n(o,s)||e),r(t),()=>{i.delete(l),0===i.size&&a&&(a(),a=null)}}}}function a(e){let r;return t(e,e=>r=e)(),r}let i=!1;function o(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:o({}),i18n:o({}),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:i=32}=e,o=await crypto.subtle.importKey("raw",r,{name:"PBKDF2"},!1,["deriveKey"]),s=await crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:a,hash:c(n)},o,{name:"AES-GCM",length:8*i},!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:i,data:o,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:o};let E=null;if(void 0!==r){const e=f(A.nonce);E=await i(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:i=e=>e>1?Math.floor(e/2):0,counterMode:o,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:o,counterStart:r,counterStep:c,timeout:s,type:"work"})}))))}catch(r){if(r instanceof Error&&!!r?.message?.includes("Out of memory")&&i){u();const r=i(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,i=null;try{i=JSON.parse(atob(e))}catch{throw new Error("Unable to parse obfuscated data.")}if(!i||"object"!=typeof i||!("parameters"in i)||!("cipher"in i))throw new Error("Invalid obfuscated data format.");const o=i.cipher;let s=null;if(!n&&"$altcha"in globalThis&&(n=globalThis.$altcha.algorithms.get(i.parameters.algorithm)),s=n?await v({challenge:i,concurrency:r,createWorker:n}):await async function(e){const{challenge:t,controller:r,counterMode:n="uint32",counterStart:a=0,counterStep:i=1,deriveKey:o,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 o(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+=i}return{counter:A,derivedKey:E,time:m(v)}}({challenge:i,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(o.iv)},c,f(o.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:i}=await S({algorithm:"PBKDF2/SHA-256",cost:5e3,deriveKey:r,counter:Math.floor(Math.random()*(a-n+1))+n,keyPrefixLength:32,...t}),o=await crypto.subtle.importKey("raw",f(i.keyPrefix),{name:"AES-GCM"},!1,["encrypt"]),s=crypto.getRandomValues(new Uint8Array(12)),c=await crypto.subtle.encrypt({name:"AES-GCM",iv:s},o,(new TextEncoder).encode(e));return btoa(JSON.stringify({parameters:{...i,keyPrefix:i.keyPrefix.slice(0,i.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))}})});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "altcha",
|
|
3
3
|
"description": "Privacy-first CAPTCHA widget, compliant with global regulations (GDPR/HIPAA/CCPA/LGDP/DPDPA/PIPL) and WCAG accessible. No tracking, self-verifying.",
|
|
4
|
-
"version": "3.0.0-beta.
|
|
4
|
+
"version": "3.0.0-beta.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Daniel Regeci",
|