altcha-lib 1.4.1 → 2.0.0-beta.2
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/LICENSE.txt +1 -1
- package/README.md +45 -149
- package/bin/cli.mjs +35 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/cjs/v2/algorithms/argon2id.d.ts +2 -0
- package/dist/cjs/v2/algorithms/argon2id.js +26 -0
- package/dist/cjs/v2/algorithms/pbkdf2.d.ts +2 -0
- package/dist/cjs/v2/algorithms/pbkdf2.js +23 -0
- package/dist/cjs/v2/algorithms/scrypt.d.ts +2 -0
- package/dist/cjs/v2/algorithms/scrypt.js +17 -0
- package/dist/cjs/v2/algorithms/sha.d.ts +2 -0
- package/dist/cjs/v2/algorithms/sha.js +35 -0
- package/dist/cjs/v2/algorithms/web/pbkdf2.d.ts +2 -0
- package/dist/cjs/v2/algorithms/web/pbkdf2.js +19 -0
- package/dist/cjs/v2/algorithms/web/sha.d.ts +2 -0
- package/dist/cjs/v2/algorithms/web/sha.js +23 -0
- package/dist/cjs/v2/capped-map.d.ts +7 -0
- package/dist/cjs/v2/capped-map.js +18 -0
- package/dist/cjs/v2/frameworks/express.d.ts +21 -0
- package/dist/cjs/v2/frameworks/express.js +77 -0
- package/dist/cjs/v2/frameworks/fastify.d.ts +26 -0
- package/dist/cjs/v2/frameworks/fastify.js +88 -0
- package/dist/cjs/v2/frameworks/h3.d.ts +37 -0
- package/dist/cjs/v2/frameworks/h3.js +89 -0
- package/dist/cjs/v2/frameworks/hono.d.ts +99 -0
- package/dist/cjs/v2/frameworks/hono.js +86 -0
- package/dist/cjs/v2/frameworks/nestjs.d.ts +79 -0
- package/dist/cjs/v2/frameworks/nestjs.js +198 -0
- package/dist/cjs/v2/frameworks/nextjs.d.ts +21 -0
- package/dist/cjs/v2/frameworks/nextjs.js +112 -0
- package/dist/cjs/v2/frameworks/shared.d.ts +8 -0
- package/dist/cjs/v2/frameworks/shared.js +121 -0
- package/dist/cjs/v2/frameworks/sveltekit.d.ts +29 -0
- package/dist/cjs/v2/frameworks/sveltekit.js +101 -0
- package/dist/cjs/v2/frameworks/types.d.ts +47 -0
- package/dist/cjs/v2/frameworks/types.js +2 -0
- package/dist/cjs/v2/helpers.d.ts +27 -0
- package/dist/cjs/v2/helpers.js +127 -0
- package/dist/cjs/v2/index.d.ts +19 -0
- package/dist/cjs/v2/index.js +28 -0
- package/dist/cjs/v2/obfuscation.d.ts +11 -0
- package/dist/cjs/v2/obfuscation.js +74 -0
- package/dist/cjs/v2/pow.d.ts +60 -0
- package/dist/cjs/v2/pow.js +287 -0
- package/dist/cjs/v2/server-signature.d.ts +12 -0
- package/dist/cjs/v2/server-signature.js +68 -0
- package/dist/cjs/v2/types.d.ts +277 -0
- package/dist/cjs/v2/types.js +18 -0
- package/dist/cjs/v2/workers/argon2id.js +7 -0
- package/dist/cjs/v2/workers/pbkdf2.js +7 -0
- package/dist/cjs/v2/workers/scrypt.d.ts +1 -0
- package/dist/cjs/v2/workers/scrypt.js +7 -0
- package/dist/cjs/v2/workers/sha.d.ts +1 -0
- package/dist/cjs/v2/workers/sha.js +7 -0
- package/dist/cjs/v2/workers/shared.d.ts +4 -0
- package/dist/cjs/v2/workers/shared.js +33 -0
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -0
- package/dist/esm/v1/types.js +1 -0
- package/dist/esm/v1/worker.d.ts +1 -0
- package/dist/esm/v2/algorithms/argon2id.d.ts +2 -0
- package/dist/esm/v2/algorithms/argon2id.js +20 -0
- package/dist/esm/v2/algorithms/pbkdf2.d.ts +2 -0
- package/dist/esm/v2/algorithms/pbkdf2.js +20 -0
- package/dist/esm/v2/algorithms/scrypt.d.ts +2 -0
- package/dist/esm/v2/algorithms/scrypt.js +14 -0
- package/dist/esm/v2/algorithms/sha.d.ts +2 -0
- package/dist/esm/v2/algorithms/sha.js +32 -0
- package/dist/esm/v2/algorithms/web/pbkdf2.d.ts +2 -0
- package/dist/esm/v2/algorithms/web/pbkdf2.js +16 -0
- package/dist/esm/v2/algorithms/web/sha.d.ts +2 -0
- package/dist/esm/v2/algorithms/web/sha.js +20 -0
- package/dist/esm/v2/capped-map.d.ts +7 -0
- package/dist/esm/v2/capped-map.js +15 -0
- package/dist/esm/v2/frameworks/express.d.ts +21 -0
- package/dist/esm/v2/frameworks/express.js +71 -0
- package/dist/esm/v2/frameworks/fastify.d.ts +26 -0
- package/dist/esm/v2/frameworks/fastify.js +82 -0
- package/dist/esm/v2/frameworks/h3.d.ts +37 -0
- package/dist/esm/v2/frameworks/h3.js +83 -0
- package/dist/esm/v2/frameworks/hono.d.ts +99 -0
- package/dist/esm/v2/frameworks/hono.js +80 -0
- package/dist/esm/v2/frameworks/nestjs.d.ts +79 -0
- package/dist/esm/v2/frameworks/nestjs.js +202 -0
- package/dist/esm/v2/frameworks/nextjs.d.ts +21 -0
- package/dist/esm/v2/frameworks/nextjs.js +106 -0
- package/dist/esm/v2/frameworks/shared.d.ts +8 -0
- package/dist/esm/v2/frameworks/shared.js +117 -0
- package/dist/esm/v2/frameworks/sveltekit.d.ts +29 -0
- package/dist/esm/v2/frameworks/sveltekit.js +95 -0
- package/dist/esm/v2/frameworks/types.d.ts +47 -0
- package/dist/esm/v2/frameworks/types.js +1 -0
- package/dist/esm/v2/helpers.d.ts +27 -0
- package/dist/esm/v2/helpers.js +112 -0
- package/dist/esm/v2/index.d.ts +19 -0
- package/dist/esm/v2/index.js +17 -0
- package/dist/esm/v2/obfuscation.d.ts +11 -0
- package/dist/esm/v2/obfuscation.js +70 -0
- package/dist/esm/v2/pow.d.ts +60 -0
- package/dist/esm/v2/pow.js +282 -0
- package/dist/esm/v2/server-signature.d.ts +12 -0
- package/dist/esm/v2/server-signature.js +63 -0
- package/dist/esm/v2/types.d.ts +277 -0
- package/dist/esm/v2/types.js +15 -0
- package/dist/esm/v2/workers/argon2id.d.ts +1 -0
- package/dist/esm/v2/workers/argon2id.js +5 -0
- package/dist/esm/v2/workers/pbkdf2.d.ts +1 -0
- package/dist/esm/v2/workers/pbkdf2.js +5 -0
- package/dist/esm/v2/workers/scrypt.d.ts +1 -0
- package/dist/esm/v2/workers/scrypt.js +5 -0
- package/dist/esm/v2/workers/sha.d.ts +1 -0
- package/dist/esm/v2/workers/sha.js +5 -0
- package/dist/esm/v2/workers/shared.d.ts +4 -0
- package/dist/esm/v2/workers/shared.js +30 -0
- package/package.json +139 -27
- package/cjs/dist/tsconfig.cjs.tsbuildinfo +0 -1
- package/cjs/package.json +0 -3
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- /package/{cjs/dist → dist/cjs/v1}/helpers.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/helpers.js +0 -0
- /package/{cjs/dist → dist/cjs/v1}/index.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/index.js +0 -0
- /package/{cjs/dist → dist/cjs/v1}/types.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/types.js +0 -0
- /package/{cjs/dist → dist/cjs/v1}/worker.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/worker.js +0 -0
- /package/dist/{types.js → cjs/v2/workers/argon2id.d.ts} +0 -0
- /package/dist/{worker.d.ts → cjs/v2/workers/pbkdf2.d.ts} +0 -0
- /package/dist/{helpers.d.ts → esm/v1/helpers.d.ts} +0 -0
- /package/dist/{helpers.js → esm/v1/helpers.js} +0 -0
- /package/dist/{index.d.ts → esm/v1/index.d.ts} +0 -0
- /package/dist/{index.js → esm/v1/index.js} +0 -0
- /package/dist/{types.d.ts → esm/v1/types.d.ts} +0 -0
- /package/dist/{worker.js → esm/v1/worker.js} +0 -0
package/LICENSE.txt
CHANGED
package/README.md
CHANGED
|
@@ -1,179 +1,75 @@
|
|
|
1
1
|
# ALTCHA JS Library
|
|
2
2
|
|
|
3
|
-
ALTCHA JS Library is a lightweight
|
|
3
|
+
ALTCHA TS/JS Library is a lightweight library for creating and verifying [ALTCHA](https://altcha.org) challenges on the server.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Get Started
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The library includes plugins for several popular frameworks to simplify integration.
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- **Express:** [`/docs/express.md`](/docs/express.md)
|
|
10
|
+
- **Fastify:** [`/docs/fastify.md`](/docs/fastify.md)
|
|
11
|
+
- **Hono:** [`/docs/hono.md`](/docs/hono.md)
|
|
12
|
+
- **NestJS:** [`/docs/nestjs.md`](/docs/nestjs.md)
|
|
13
|
+
- **Next.js:** [`/docs/nextjs.md`](/docs/nextjs.md)
|
|
14
|
+
- **Nuxt:** [`/docs/nuxt.md`](/docs/nuxt.md)
|
|
15
|
+
- **SvelteKit:** [`/docs/sveltekit.md`](/docs/sveltekit.md)
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
If your framework is not listed, see the [Advanced Usage](/docs/advanced-usage.md) guide for custom integrations.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
import { createChallenge, verifySolution } from 'altcha-lib';
|
|
19
|
+
## Documentation
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
- Advanced Usage: [`/docs/advanced-usage.md`](/docs/advanced-usage.md)
|
|
22
|
+
- Algorithms: [`/docs/algorithms.md`](/docs/algorithms.md)
|
|
23
|
+
- Configuration Options: [`/docs/configuration-options.md`](/docs/configuration-options.md)
|
|
24
|
+
- Obfuscation: [`/docs/obfuscation.md`](/docs/obfuscation.md)
|
|
25
|
+
- Store: [`/docs/store.md`](/docs/store.md)
|
|
26
|
+
- Usage with ALTCHA Sentinel: [`/docs/server-signatures.md`](/docs/server-signatures.md)
|
|
27
|
+
- Using the `/verify` Route: [`/docs/verify-route.md`](/docs/verify-route.md)
|
|
28
|
+
- Workers: [`/docs/workers.md`](/docs/workers.md)
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
const challenge = await createChallenge({
|
|
24
|
-
hmacKey,
|
|
25
|
-
maxNumber: 100000, // the maximum random number
|
|
26
|
-
});
|
|
30
|
+
## Compatibility
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
| Runtime | Version | Notes |
|
|
33
|
+
| ----------------- | ------: | ----------------------------------------------- |
|
|
34
|
+
| Node.js | 20+ | Argon2 available natively in 24.7+ |
|
|
35
|
+
| Bun | 1+ | Argon2 not available natively |
|
|
36
|
+
| Deno | 2+ | Argon2 not available natively |
|
|
37
|
+
| WinterCG runtimes | — | Use WebCrypto [algorithms](/docs/algorithms.md) |
|
|
31
38
|
|
|
32
|
-
###
|
|
39
|
+
### Run examples
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
**Express example (Node.js with `tsx`):**
|
|
35
42
|
|
|
36
|
-
```
|
|
37
|
-
|
|
43
|
+
```sh
|
|
44
|
+
npx tsx examples/express-example.ts
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
**Hono example (Bun):**
|
|
41
48
|
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
globalThis.crypto = webcrypto;
|
|
49
|
+
```sh
|
|
50
|
+
bun run --port 3000 examples/hono-example.ts
|
|
46
51
|
```
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
### `createChallenge(options)`
|
|
51
|
-
|
|
52
|
-
Creates a new challenge for ALTCHA.
|
|
53
|
-
|
|
54
|
-
Parameters:
|
|
55
|
-
|
|
56
|
-
- `options: ChallengeOptions`:
|
|
57
|
-
- `algorithm?: string`: Algorithm to use (`SHA-1`, `SHA-256`, `SHA-512`, default: `SHA-256`).
|
|
58
|
-
- `expires?: Date`: Optional `expires` time (as `Date` set into the future date).
|
|
59
|
-
- `hmacKey: string` (required): Signature HMAC key.
|
|
60
|
-
- `maxnumber?: number`: Optional maximum number for the random number generator (defaults to 1,000,000).
|
|
61
|
-
- `number?: number`: Optional number to use. If not provided, a random number will be generated.
|
|
62
|
-
- `params?: Record<string, string>`: Optional parameters to be added to the salt as URL-encoded query string. Use `extractParams()` to read them.
|
|
63
|
-
- `salt?: string`: Optional salt string. If not provided, a random salt will be generated.
|
|
64
|
-
- `saltLength?: number`: Optional maximum lenght of the random salt (in bytes, defaults to 12).
|
|
65
|
-
|
|
66
|
-
Returns: `Promise<Challenge>`
|
|
67
|
-
|
|
68
|
-
### `extractParams(payload)`
|
|
69
|
-
|
|
70
|
-
Extracts optional parameters from the challenge or payload.
|
|
71
|
-
|
|
72
|
-
Parameters:
|
|
73
|
-
|
|
74
|
-
- `payload: string | Payload | Challenge`
|
|
75
|
-
|
|
76
|
-
Returns: `Record<string, string>`
|
|
77
|
-
|
|
78
|
-
### `verifySolution(payload, hmacKey, checkExpires = true)`
|
|
79
|
-
|
|
80
|
-
Verifies an ALTCHA solution. The payload can be a Base64-encoded JSON payload (as submitted by the widget) or an object.
|
|
81
|
-
|
|
82
|
-
Parameters:
|
|
83
|
-
|
|
84
|
-
- `payload: string | Payload`
|
|
85
|
-
- `hmacKey: string`
|
|
86
|
-
- `checkExpires: boolean = true`: Whether to perform a check on the optional `expires` parameter. Will return `false` if challenge expired.
|
|
87
|
-
|
|
88
|
-
Returns: `Promise<boolean>`
|
|
53
|
+
**Hono example (Deno):**
|
|
89
54
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
Parameters:
|
|
95
|
-
|
|
96
|
-
- `payload: string | ServerSignaturePayload`
|
|
97
|
-
- `hmacKey: string`
|
|
98
|
-
|
|
99
|
-
Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`
|
|
100
|
-
|
|
101
|
-
### `verifyFieldsHash(formData, fields, fieldsHash, algorithm?)`
|
|
102
|
-
|
|
103
|
-
Verifies the hash of form fields returned by the Spam Filter.
|
|
104
|
-
|
|
105
|
-
Parameters:
|
|
106
|
-
|
|
107
|
-
- `formData: FormData | Record<string, unknown>`
|
|
108
|
-
- `fields: string[]`
|
|
109
|
-
- `fieldsHash: string`
|
|
110
|
-
- `algorithm: Algorithm = 'SHA-256'`
|
|
111
|
-
|
|
112
|
-
Returns: `Promise<boolean>`
|
|
113
|
-
|
|
114
|
-
### `solveChallenge(challenge, salt, algorithm?, max?, start?)`
|
|
115
|
-
|
|
116
|
-
Finds a solution to the given challenge.
|
|
117
|
-
|
|
118
|
-
Parameters:
|
|
119
|
-
|
|
120
|
-
- `challenge: string` (required): The challenge hash.
|
|
121
|
-
- `salt: string` (required): The challenge salt.
|
|
122
|
-
- `algorithm?: string`: Optional algorithm (default: `SHA-256`).
|
|
123
|
-
- `maxnumber?: string`: Optional `maxnumber` to iterate to (default: 1e6).
|
|
124
|
-
- `start?: string`: Optional starting number (default: 0).
|
|
125
|
-
|
|
126
|
-
Returns: `{ controller: AbortController, promise: Promise<Solution | null> }`
|
|
127
|
-
|
|
128
|
-
### `solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm?, max?, start?)`
|
|
55
|
+
```sh
|
|
56
|
+
deno serve --allow-net --sloppy-imports --port 3000 examples/hono-example.ts
|
|
57
|
+
```
|
|
129
58
|
|
|
130
|
-
|
|
59
|
+
## Breaking Changes (v2)
|
|
131
60
|
|
|
132
|
-
|
|
61
|
+
Version 2 introduces a new proof-of-work mechanism and challenge format. See the [PoW documentation](https://playground.altcha.org/#/about) for details.
|
|
133
62
|
|
|
134
|
-
|
|
135
|
-
- `concurrency: number` (required): The concurrency (number of workers).
|
|
136
|
-
- `challenge: string` (required): The challenge hash.
|
|
137
|
-
- `salt: string` (required): The challenge salt.
|
|
138
|
-
- `algorithm?: string`: Optional algorithm (default: `SHA-256`).
|
|
139
|
-
- `maxnumber?: string`: Optional `maxnumber` to iterate to (default: 1e6).
|
|
140
|
-
- `start?: string`: Optional starting number (default: 0).
|
|
63
|
+
While the overall usage remains similar, the library has been refactored and several function signatures have changed. Using the provided framework plugins is recommended. For unsupported frameworks or custom setups, refer to the [Advanced Usage](/docs/advanced-usage.md) guide.
|
|
141
64
|
|
|
142
|
-
|
|
65
|
+
### Compatibility with v1
|
|
143
66
|
|
|
144
|
-
|
|
67
|
+
The API for the previous PoW version (v1) remains available under the `altcha-lib/v1` path:
|
|
145
68
|
|
|
146
69
|
```ts
|
|
147
|
-
import {
|
|
148
|
-
|
|
149
|
-
const solution = await solveChallengeWorkers(
|
|
150
|
-
'altcha-lib/worker', // Worker script URL or path
|
|
151
|
-
8, // Spawn 8 workers
|
|
152
|
-
challenge,
|
|
153
|
-
salt,
|
|
154
|
-
);
|
|
70
|
+
import { createChallenge } from 'altcha-lib/v1';
|
|
155
71
|
```
|
|
156
72
|
|
|
157
|
-
## Benchmarks
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
> solveChallenge()
|
|
161
|
-
- n = 1,000............................... 312 ops/s ±2.90%
|
|
162
|
-
- n = 10,000.............................. 31 ops/s ±1.50%
|
|
163
|
-
- n = 50,000.............................. 6 ops/s ±0.82%
|
|
164
|
-
- n = 100,000............................. 3 ops/s ±0.37%
|
|
165
|
-
- n = 500,000............................. 0 ops/s ±0.31%
|
|
166
|
-
|
|
167
|
-
> solveChallengeWorkers() (8 workers)
|
|
168
|
-
- n = 1,000............................... 62 ops/s ±3.99%
|
|
169
|
-
- n = 10,000.............................. 31 ops/s ±6.83%
|
|
170
|
-
- n = 50,000.............................. 11 ops/s ±4.00%
|
|
171
|
-
- n = 100,000............................. 7 ops/s ±2.32%
|
|
172
|
-
- n = 500,000............................. 1 ops/s ±1.89%
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
Run with Bun on MacBook Pro M3-Pro. See [/benchmark](/benchmark/) folder for more details.
|
|
176
|
-
|
|
177
73
|
## License
|
|
178
74
|
|
|
179
|
-
MIT
|
|
75
|
+
MIT
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { obfuscate, deobfuscate } from 'altcha-lib/obfuscation';
|
|
4
|
+
|
|
5
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
if (!command || !['obfuscate', 'deobfuscate'].includes(command)) {
|
|
8
|
+
console.error('Usage: altcha-lib <obfuscate|deobfuscate> [data]');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function readStdin() {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
let buf = '';
|
|
15
|
+
process.stdin.setEncoding('utf-8');
|
|
16
|
+
process.stdin.on('data', (chunk) => buf += chunk);
|
|
17
|
+
process.stdin.on('end', () => resolve(buf.trim()));
|
|
18
|
+
process.stdin.on('error', reject);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const data = rest.length ? rest.join(' ') : await readStdin();
|
|
24
|
+
|
|
25
|
+
if (!data) {
|
|
26
|
+
console.error(`Error: No data provided for ${command}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const result = command === 'obfuscate' ? await obfuscate(data) : await deobfuscate(data);
|
|
31
|
+
console.log(result);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error(`Error during ${command}: ${err.message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../../src/v1/helpers.ts","../../src/v1/index.ts","../../src/v1/types.ts","../../src/v1/worker.ts","../../src/v2/capped-map.ts","../../src/v2/helpers.ts","../../src/v2/index.ts","../../src/v2/obfuscation.ts","../../src/v2/pow.ts","../../src/v2/server-signature.ts","../../src/v2/types.ts","../../src/v2/algorithms/argon2id.ts","../../src/v2/algorithms/pbkdf2.ts","../../src/v2/algorithms/scrypt.ts","../../src/v2/algorithms/sha.ts","../../src/v2/algorithms/web/pbkdf2.ts","../../src/v2/algorithms/web/sha.ts","../../src/v2/frameworks/express.ts","../../src/v2/frameworks/fastify.ts","../../src/v2/frameworks/h3.ts","../../src/v2/frameworks/hono.ts","../../src/v2/frameworks/nestjs.ts","../../src/v2/frameworks/nextjs.ts","../../src/v2/frameworks/shared.ts","../../src/v2/frameworks/sveltekit.ts","../../src/v2/frameworks/types.ts","../../src/v2/workers/argon2id.ts","../../src/v2/workers/pbkdf2.ts","../../src/v2/workers/scrypt.ts","../../src/v2/workers/sha.ts","../../src/v2/workers/shared.ts"],"version":"5.9.3"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.deriveKey = deriveKey;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const node_util_1 = require("node:util");
|
|
9
|
+
const argon2Async = 'argon2' in node_crypto_1.default
|
|
10
|
+
? (0, node_util_1.promisify)(node_crypto_1.default.argon2)
|
|
11
|
+
: async () => {
|
|
12
|
+
throw new Error('Argon2 not supported in node:crypto.');
|
|
13
|
+
};
|
|
14
|
+
async function deriveKey(parameters, salt, password) {
|
|
15
|
+
return {
|
|
16
|
+
parameters: {},
|
|
17
|
+
derivedKey: await argon2Async('argon2id', {
|
|
18
|
+
memory: parameters.memoryCost,
|
|
19
|
+
parallelism: parameters.parallelism || 1,
|
|
20
|
+
nonce: salt,
|
|
21
|
+
message: password,
|
|
22
|
+
passes: parameters.cost,
|
|
23
|
+
tagLength: parameters.keyLength,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveKey = deriveKey;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const pbkdf2Async = (0, node_util_1.promisify)(node_crypto_1.pbkdf2);
|
|
7
|
+
function getDigest(algorithm) {
|
|
8
|
+
switch (algorithm) {
|
|
9
|
+
case 'PBKDF2/SHA-512':
|
|
10
|
+
return 'sha512';
|
|
11
|
+
case 'PBKDF2/SHA-384':
|
|
12
|
+
return 'sha384';
|
|
13
|
+
case 'PBKDF2/SHA-256':
|
|
14
|
+
default:
|
|
15
|
+
return 'sha256';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function deriveKey(parameters, salt, password) {
|
|
19
|
+
return {
|
|
20
|
+
parameters: {},
|
|
21
|
+
derivedKey: await pbkdf2Async(password, salt, parameters.cost, parameters.keyLength, getDigest(parameters.algorithm)),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveKey = deriveKey;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const scryptAsync = (0, node_util_1.promisify)(node_crypto_1.scrypt);
|
|
7
|
+
async function deriveKey(parameters, salt, password) {
|
|
8
|
+
return {
|
|
9
|
+
parameters: {},
|
|
10
|
+
derivedKey: await scryptAsync(password, salt, parameters.keyLength, {
|
|
11
|
+
blockSize: parameters.memoryCost,
|
|
12
|
+
cost: parameters.cost,
|
|
13
|
+
maxmem: 2 * 128 * parameters.cost * parameters.memoryCost,
|
|
14
|
+
parallelization: parameters.parallelism || 1,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveKey = deriveKey;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const helpers_js_1 = require("../helpers.js");
|
|
6
|
+
function getDigest(algorithm) {
|
|
7
|
+
switch (algorithm) {
|
|
8
|
+
case 'SHA-512':
|
|
9
|
+
return 'sha512';
|
|
10
|
+
case 'SHA-384':
|
|
11
|
+
return 'sha384';
|
|
12
|
+
case 'SHA-256':
|
|
13
|
+
default:
|
|
14
|
+
return 'sha256';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function deriveKey(parameters, salt, password) {
|
|
18
|
+
const { algorithm, keyLength = 32 } = parameters;
|
|
19
|
+
const iterations = Math.max(1, parameters.cost);
|
|
20
|
+
let data = undefined;
|
|
21
|
+
let derivedKey = undefined;
|
|
22
|
+
for (let i = 0; i < iterations; i++) {
|
|
23
|
+
if (i === 0) {
|
|
24
|
+
data = (0, helpers_js_1.concatBuffers)(salt, password);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
data = derivedKey;
|
|
28
|
+
}
|
|
29
|
+
derivedKey = (0, node_crypto_1.createHash)(getDigest(algorithm)).update(data).digest();
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
parameters: {},
|
|
33
|
+
derivedKey: derivedKey.subarray(0, keyLength),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveKey = deriveKey;
|
|
4
|
+
async function deriveKey(parameters, salt, password) {
|
|
5
|
+
const { cost, keyLength = 32 } = parameters;
|
|
6
|
+
const hash = parameters.algorithm.startsWith('PBKDF2/')
|
|
7
|
+
? parameters.algorithm.slice(7)
|
|
8
|
+
: 'SHA-256';
|
|
9
|
+
const passwordKey = await crypto.subtle.importKey('raw', password, { name: 'PBKDF2' }, false, ['deriveKey']);
|
|
10
|
+
const derivedKey = await crypto.subtle.deriveKey({
|
|
11
|
+
name: 'PBKDF2',
|
|
12
|
+
salt: salt,
|
|
13
|
+
iterations: cost,
|
|
14
|
+
hash,
|
|
15
|
+
}, passwordKey, { name: 'AES-GCM', length: keyLength * 8 }, true, ['encrypt']);
|
|
16
|
+
return {
|
|
17
|
+
derivedKey: new Uint8Array(await crypto.subtle.exportKey('raw', derivedKey)),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveKey = deriveKey;
|
|
4
|
+
const helpers_js_1 = require("../../helpers.js");
|
|
5
|
+
async function deriveKey(parameters, salt, password) {
|
|
6
|
+
const { algorithm, keyLength = 32 } = parameters;
|
|
7
|
+
const iterations = Math.max(1, parameters.cost);
|
|
8
|
+
let data = undefined;
|
|
9
|
+
let derivedKey = undefined;
|
|
10
|
+
for (let i = 0; i < iterations; i++) {
|
|
11
|
+
if (i === 0) {
|
|
12
|
+
data = (0, helpers_js_1.concatBuffers)(salt, password);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
data = derivedKey;
|
|
16
|
+
}
|
|
17
|
+
derivedKey = new Uint8Array((await crypto.subtle.digest(algorithm, data)).slice(0, keyLength));
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
parameters: {},
|
|
21
|
+
derivedKey: derivedKey,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CappedMap = void 0;
|
|
4
|
+
class CappedMap extends Map {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
super();
|
|
7
|
+
const { maxSize } = options;
|
|
8
|
+
this.maxSize = maxSize;
|
|
9
|
+
}
|
|
10
|
+
set(key, value) {
|
|
11
|
+
if (this.size >= this.maxSize && !this.has(key)) {
|
|
12
|
+
this.delete(this.keys().next().value);
|
|
13
|
+
}
|
|
14
|
+
super.set(key, value);
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.CappedMap = CappedMap;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { randomInt } from '../helpers.js';
|
|
3
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
4
|
+
import { CappedMap } from '../capped-map.js';
|
|
5
|
+
import type { AltchaMiddlewareOptions, AltchaOptions, AltchaResult } from './types.js';
|
|
6
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
7
|
+
export type { AltchaOptions, AltchaResult };
|
|
8
|
+
export declare function create(options: AltchaOptions): {
|
|
9
|
+
challengeHandler: (req: Request, res: Response, next: NextFunction) => void;
|
|
10
|
+
verifyHandler: (req: Request, res: Response, next: NextFunction) => void;
|
|
11
|
+
getPayloadFromRequest: (req: Request, cookieName?: string) => Promise<string | undefined>;
|
|
12
|
+
middleware: (options?: AltchaMiddlewareOptions) => (req: Request, res: Response, next: NextFunction) => void;
|
|
13
|
+
verify: typeof verify;
|
|
14
|
+
};
|
|
15
|
+
declare const _default: {
|
|
16
|
+
CappedMap: typeof CappedMap;
|
|
17
|
+
create: typeof create;
|
|
18
|
+
deriveHmacKeySecret: typeof deriveHmacKeySecret;
|
|
19
|
+
randomInt: typeof randomInt;
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.randomInt = exports.deriveHmacKeySecret = exports.CappedMap = void 0;
|
|
4
|
+
exports.create = create;
|
|
5
|
+
const pow_js_1 = require("../pow.js");
|
|
6
|
+
const helpers_js_1 = require("../helpers.js");
|
|
7
|
+
Object.defineProperty(exports, "randomInt", { enumerable: true, get: function () { return helpers_js_1.randomInt; } });
|
|
8
|
+
const shared_js_1 = require("./shared.js");
|
|
9
|
+
Object.defineProperty(exports, "deriveHmacKeySecret", { enumerable: true, get: function () { return shared_js_1.deriveHmacKeySecret; } });
|
|
10
|
+
const capped_map_js_1 = require("../capped-map.js");
|
|
11
|
+
Object.defineProperty(exports, "CappedMap", { enumerable: true, get: function () { return capped_map_js_1.CappedMap; } });
|
|
12
|
+
const asyncHandler = (fn) => (req, res, next) => {
|
|
13
|
+
fn(req, res, next).catch(next);
|
|
14
|
+
};
|
|
15
|
+
function create(options) {
|
|
16
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
17
|
+
const challengeHandler = asyncHandler(async (req, res) => {
|
|
18
|
+
const challenge = await (0, pow_js_1.createChallenge)({
|
|
19
|
+
deriveKey,
|
|
20
|
+
hmacSignatureSecret,
|
|
21
|
+
hmacKeySignatureSecret,
|
|
22
|
+
...createChallengeParameters(),
|
|
23
|
+
});
|
|
24
|
+
res.json({
|
|
25
|
+
configuration: setCookie
|
|
26
|
+
? {
|
|
27
|
+
setCookie,
|
|
28
|
+
}
|
|
29
|
+
: undefined,
|
|
30
|
+
...challenge,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
const verifyHandler = asyncHandler(async (req, res) => {
|
|
34
|
+
const payload = await getPayloadFromRequest(req);
|
|
35
|
+
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
36
|
+
res.json(result);
|
|
37
|
+
});
|
|
38
|
+
const getPayloadFromRequest = async (req, cookieName) => {
|
|
39
|
+
if (cookieName) {
|
|
40
|
+
return req.cookies?.[cookieName];
|
|
41
|
+
}
|
|
42
|
+
return req.body?.[fieldName];
|
|
43
|
+
};
|
|
44
|
+
const middleware = (options = {}) => {
|
|
45
|
+
const { throwOnFailure = true } = options;
|
|
46
|
+
return asyncHandler(async (req, res, next) => {
|
|
47
|
+
const payload = await getPayloadFromRequest(req, setCookie?.name);
|
|
48
|
+
const { error, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
49
|
+
res.locals.altcha = {
|
|
50
|
+
error,
|
|
51
|
+
payload: resultPayload,
|
|
52
|
+
verification,
|
|
53
|
+
};
|
|
54
|
+
if (setCookie) {
|
|
55
|
+
res.clearCookie(setCookie.name);
|
|
56
|
+
}
|
|
57
|
+
if (error && throwOnFailure) {
|
|
58
|
+
res.status(403).json({ error });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
next();
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
challengeHandler,
|
|
66
|
+
verifyHandler,
|
|
67
|
+
getPayloadFromRequest,
|
|
68
|
+
middleware,
|
|
69
|
+
verify: shared_js_1.verify,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
exports.default = {
|
|
73
|
+
CappedMap: capped_map_js_1.CappedMap,
|
|
74
|
+
create,
|
|
75
|
+
deriveHmacKeySecret: shared_js_1.deriveHmacKeySecret,
|
|
76
|
+
randomInt: helpers_js_1.randomInt,
|
|
77
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { randomInt } from '../helpers.js';
|
|
2
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
3
|
+
import { CappedMap } from '../capped-map.js';
|
|
4
|
+
import type { AltchaMiddlewareOptions, AltchaOptions, AltchaResult } from './types.js';
|
|
5
|
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
6
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
7
|
+
export type { AltchaOptions, AltchaResult };
|
|
8
|
+
declare module 'fastify' {
|
|
9
|
+
interface FastifyRequest {
|
|
10
|
+
altcha?: AltchaResult;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export declare function create(options: AltchaOptions): {
|
|
14
|
+
challengeHandler: (request: FastifyRequest, reply: FastifyReply) => Promise<never>;
|
|
15
|
+
verifyHandler: (request: FastifyRequest, reply: FastifyReply) => Promise<never>;
|
|
16
|
+
getPayloadFromRequest: (request: FastifyRequest, cookieName?: string) => Promise<string | undefined>;
|
|
17
|
+
middleware: (options?: AltchaMiddlewareOptions) => (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
|
|
18
|
+
verify: typeof verify;
|
|
19
|
+
};
|
|
20
|
+
declare const _default: {
|
|
21
|
+
CappedMap: typeof CappedMap;
|
|
22
|
+
create: typeof create;
|
|
23
|
+
deriveHmacKeySecret: typeof deriveHmacKeySecret;
|
|
24
|
+
randomInt: typeof randomInt;
|
|
25
|
+
};
|
|
26
|
+
export default _default;
|