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.
Files changed (133) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +45 -149
  3. package/bin/cli.mjs +35 -0
  4. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  5. package/dist/cjs/v2/algorithms/argon2id.d.ts +2 -0
  6. package/dist/cjs/v2/algorithms/argon2id.js +26 -0
  7. package/dist/cjs/v2/algorithms/pbkdf2.d.ts +2 -0
  8. package/dist/cjs/v2/algorithms/pbkdf2.js +23 -0
  9. package/dist/cjs/v2/algorithms/scrypt.d.ts +2 -0
  10. package/dist/cjs/v2/algorithms/scrypt.js +17 -0
  11. package/dist/cjs/v2/algorithms/sha.d.ts +2 -0
  12. package/dist/cjs/v2/algorithms/sha.js +35 -0
  13. package/dist/cjs/v2/algorithms/web/pbkdf2.d.ts +2 -0
  14. package/dist/cjs/v2/algorithms/web/pbkdf2.js +19 -0
  15. package/dist/cjs/v2/algorithms/web/sha.d.ts +2 -0
  16. package/dist/cjs/v2/algorithms/web/sha.js +23 -0
  17. package/dist/cjs/v2/capped-map.d.ts +7 -0
  18. package/dist/cjs/v2/capped-map.js +18 -0
  19. package/dist/cjs/v2/frameworks/express.d.ts +21 -0
  20. package/dist/cjs/v2/frameworks/express.js +77 -0
  21. package/dist/cjs/v2/frameworks/fastify.d.ts +26 -0
  22. package/dist/cjs/v2/frameworks/fastify.js +88 -0
  23. package/dist/cjs/v2/frameworks/h3.d.ts +37 -0
  24. package/dist/cjs/v2/frameworks/h3.js +89 -0
  25. package/dist/cjs/v2/frameworks/hono.d.ts +99 -0
  26. package/dist/cjs/v2/frameworks/hono.js +86 -0
  27. package/dist/cjs/v2/frameworks/nestjs.d.ts +79 -0
  28. package/dist/cjs/v2/frameworks/nestjs.js +198 -0
  29. package/dist/cjs/v2/frameworks/nextjs.d.ts +21 -0
  30. package/dist/cjs/v2/frameworks/nextjs.js +112 -0
  31. package/dist/cjs/v2/frameworks/shared.d.ts +8 -0
  32. package/dist/cjs/v2/frameworks/shared.js +121 -0
  33. package/dist/cjs/v2/frameworks/sveltekit.d.ts +29 -0
  34. package/dist/cjs/v2/frameworks/sveltekit.js +101 -0
  35. package/dist/cjs/v2/frameworks/types.d.ts +47 -0
  36. package/dist/cjs/v2/frameworks/types.js +2 -0
  37. package/dist/cjs/v2/helpers.d.ts +27 -0
  38. package/dist/cjs/v2/helpers.js +127 -0
  39. package/dist/cjs/v2/index.d.ts +19 -0
  40. package/dist/cjs/v2/index.js +28 -0
  41. package/dist/cjs/v2/obfuscation.d.ts +11 -0
  42. package/dist/cjs/v2/obfuscation.js +74 -0
  43. package/dist/cjs/v2/pow.d.ts +60 -0
  44. package/dist/cjs/v2/pow.js +287 -0
  45. package/dist/cjs/v2/server-signature.d.ts +12 -0
  46. package/dist/cjs/v2/server-signature.js +68 -0
  47. package/dist/cjs/v2/types.d.ts +277 -0
  48. package/dist/cjs/v2/types.js +18 -0
  49. package/dist/cjs/v2/workers/argon2id.js +7 -0
  50. package/dist/cjs/v2/workers/pbkdf2.js +7 -0
  51. package/dist/cjs/v2/workers/scrypt.d.ts +1 -0
  52. package/dist/cjs/v2/workers/scrypt.js +7 -0
  53. package/dist/cjs/v2/workers/sha.d.ts +1 -0
  54. package/dist/cjs/v2/workers/sha.js +7 -0
  55. package/dist/cjs/v2/workers/shared.d.ts +4 -0
  56. package/dist/cjs/v2/workers/shared.js +33 -0
  57. package/dist/esm/tsconfig.build.tsbuildinfo +1 -0
  58. package/dist/esm/v1/types.js +1 -0
  59. package/dist/esm/v1/worker.d.ts +1 -0
  60. package/dist/esm/v2/algorithms/argon2id.d.ts +2 -0
  61. package/dist/esm/v2/algorithms/argon2id.js +20 -0
  62. package/dist/esm/v2/algorithms/pbkdf2.d.ts +2 -0
  63. package/dist/esm/v2/algorithms/pbkdf2.js +20 -0
  64. package/dist/esm/v2/algorithms/scrypt.d.ts +2 -0
  65. package/dist/esm/v2/algorithms/scrypt.js +14 -0
  66. package/dist/esm/v2/algorithms/sha.d.ts +2 -0
  67. package/dist/esm/v2/algorithms/sha.js +32 -0
  68. package/dist/esm/v2/algorithms/web/pbkdf2.d.ts +2 -0
  69. package/dist/esm/v2/algorithms/web/pbkdf2.js +16 -0
  70. package/dist/esm/v2/algorithms/web/sha.d.ts +2 -0
  71. package/dist/esm/v2/algorithms/web/sha.js +20 -0
  72. package/dist/esm/v2/capped-map.d.ts +7 -0
  73. package/dist/esm/v2/capped-map.js +15 -0
  74. package/dist/esm/v2/frameworks/express.d.ts +21 -0
  75. package/dist/esm/v2/frameworks/express.js +71 -0
  76. package/dist/esm/v2/frameworks/fastify.d.ts +26 -0
  77. package/dist/esm/v2/frameworks/fastify.js +82 -0
  78. package/dist/esm/v2/frameworks/h3.d.ts +37 -0
  79. package/dist/esm/v2/frameworks/h3.js +83 -0
  80. package/dist/esm/v2/frameworks/hono.d.ts +99 -0
  81. package/dist/esm/v2/frameworks/hono.js +80 -0
  82. package/dist/esm/v2/frameworks/nestjs.d.ts +79 -0
  83. package/dist/esm/v2/frameworks/nestjs.js +202 -0
  84. package/dist/esm/v2/frameworks/nextjs.d.ts +21 -0
  85. package/dist/esm/v2/frameworks/nextjs.js +106 -0
  86. package/dist/esm/v2/frameworks/shared.d.ts +8 -0
  87. package/dist/esm/v2/frameworks/shared.js +117 -0
  88. package/dist/esm/v2/frameworks/sveltekit.d.ts +29 -0
  89. package/dist/esm/v2/frameworks/sveltekit.js +95 -0
  90. package/dist/esm/v2/frameworks/types.d.ts +47 -0
  91. package/dist/esm/v2/frameworks/types.js +1 -0
  92. package/dist/esm/v2/helpers.d.ts +27 -0
  93. package/dist/esm/v2/helpers.js +112 -0
  94. package/dist/esm/v2/index.d.ts +19 -0
  95. package/dist/esm/v2/index.js +17 -0
  96. package/dist/esm/v2/obfuscation.d.ts +11 -0
  97. package/dist/esm/v2/obfuscation.js +70 -0
  98. package/dist/esm/v2/pow.d.ts +60 -0
  99. package/dist/esm/v2/pow.js +282 -0
  100. package/dist/esm/v2/server-signature.d.ts +12 -0
  101. package/dist/esm/v2/server-signature.js +63 -0
  102. package/dist/esm/v2/types.d.ts +277 -0
  103. package/dist/esm/v2/types.js +15 -0
  104. package/dist/esm/v2/workers/argon2id.d.ts +1 -0
  105. package/dist/esm/v2/workers/argon2id.js +5 -0
  106. package/dist/esm/v2/workers/pbkdf2.d.ts +1 -0
  107. package/dist/esm/v2/workers/pbkdf2.js +5 -0
  108. package/dist/esm/v2/workers/scrypt.d.ts +1 -0
  109. package/dist/esm/v2/workers/scrypt.js +5 -0
  110. package/dist/esm/v2/workers/sha.d.ts +1 -0
  111. package/dist/esm/v2/workers/sha.js +5 -0
  112. package/dist/esm/v2/workers/shared.d.ts +4 -0
  113. package/dist/esm/v2/workers/shared.js +30 -0
  114. package/package.json +139 -27
  115. package/cjs/dist/tsconfig.cjs.tsbuildinfo +0 -1
  116. package/cjs/package.json +0 -3
  117. package/dist/tsconfig.build.tsbuildinfo +0 -1
  118. /package/{cjs/dist → dist/cjs/v1}/helpers.d.ts +0 -0
  119. /package/{cjs/dist → dist/cjs/v1}/helpers.js +0 -0
  120. /package/{cjs/dist → dist/cjs/v1}/index.d.ts +0 -0
  121. /package/{cjs/dist → dist/cjs/v1}/index.js +0 -0
  122. /package/{cjs/dist → dist/cjs/v1}/types.d.ts +0 -0
  123. /package/{cjs/dist → dist/cjs/v1}/types.js +0 -0
  124. /package/{cjs/dist → dist/cjs/v1}/worker.d.ts +0 -0
  125. /package/{cjs/dist → dist/cjs/v1}/worker.js +0 -0
  126. /package/dist/{types.js → cjs/v2/workers/argon2id.d.ts} +0 -0
  127. /package/dist/{worker.d.ts → cjs/v2/workers/pbkdf2.d.ts} +0 -0
  128. /package/dist/{helpers.d.ts → esm/v1/helpers.d.ts} +0 -0
  129. /package/dist/{helpers.js → esm/v1/helpers.js} +0 -0
  130. /package/dist/{index.d.ts → esm/v1/index.d.ts} +0 -0
  131. /package/dist/{index.js → esm/v1/index.js} +0 -0
  132. /package/dist/{types.d.ts → esm/v1/types.d.ts} +0 -0
  133. /package/dist/{worker.js → esm/v1/worker.js} +0 -0
package/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Daniel Regeci
3
+ Copyright (c) 2023-2026 Daniel Regeci
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,179 +1,75 @@
1
1
  # ALTCHA JS Library
2
2
 
3
- ALTCHA JS Library is a lightweight, zero-dependency library designed for creating and verifying [ALTCHA](https://altcha.org) challenges.
3
+ ALTCHA TS/JS Library is a lightweight library for creating and verifying [ALTCHA](https://altcha.org) challenges on the server.
4
4
 
5
- ## Compatibility
5
+ ## Get Started
6
6
 
7
- This library utilizes [Web Crypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto).
7
+ The library includes plugins for several popular frameworks to simplify integration.
8
8
 
9
- - Node.js 16+
10
- - Bun 1+
11
- - Deno 1+
12
- - WinterCG-compatible runtimes
13
- - All modern browsers
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
- ## Usage
17
+ If your framework is not listed, see the [Advanced Usage](/docs/advanced-usage.md) guide for custom integrations.
16
18
 
17
- ```ts
18
- import { createChallenge, verifySolution } from 'altcha-lib';
19
+ ## Documentation
19
20
 
20
- const hmacKey = 'secret hmac key';
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
- // Create a new challenge and send it to the client:
23
- const challenge = await createChallenge({
24
- hmacKey,
25
- maxNumber: 100000, // the maximum random number
26
- });
30
+ ## Compatibility
27
31
 
28
- // When submitted, verify the payload:
29
- const ok = await verifySolution(payload, hmacKey);
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
- ### Usage with Node.js 16
39
+ ### Run examples
33
40
 
34
- In Node.js version 16, there is no global reference to crypto by default. To use this library, you need to add the following code to your codebase:
41
+ **Express example (Node.js with `tsx`):**
35
42
 
36
- ```ts
37
- globalThis.crypto = require('node:crypto').webcrypto;
43
+ ```sh
44
+ npx tsx examples/express-example.ts
38
45
  ```
39
46
 
40
- Or with `import` syntax:
47
+ **Hono example (Bun):**
41
48
 
42
- ```ts
43
- import { webcrypto } from 'node:crypto';
44
-
45
- globalThis.crypto = webcrypto;
49
+ ```sh
50
+ bun run --port 3000 examples/hono-example.ts
46
51
  ```
47
52
 
48
- ## API
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
- ### `verifyServerSignature(payload, hmacKey)`
91
-
92
- Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.
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
- Finds a solution to the given challenge with [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) running concurrently.
59
+ ## Breaking Changes (v2)
131
60
 
132
- Parameters:
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
- - `workerScript: string` (required): The path or URL of the worker script.
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
- Returns: `Promise<Solution | null>`
65
+ ### Compatibility with v1
143
66
 
144
- Usage with `altcha-lib/worker`:
67
+ The API for the previous PoW version (v1) remains available under the `altcha-lib/v1` path:
145
68
 
146
69
  ```ts
147
- import { solveChallengeWorkers } from 'altcha-lib';
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,2 @@
1
+ import type { ChallengeParameters, DeriveKeyFunctionResult } from '../types.js';
2
+ export declare function deriveKey(parameters: ChallengeParameters, salt: Uint8Array, password: Uint8Array): Promise<DeriveKeyFunctionResult>;
@@ -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,2 @@
1
+ import type { ChallengeParameters, DeriveKeyFunctionResult } from '../types.js';
2
+ export declare function deriveKey(parameters: ChallengeParameters, salt: Uint8Array, password: Uint8Array): Promise<DeriveKeyFunctionResult>;
@@ -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,2 @@
1
+ import type { ChallengeParameters, DeriveKeyFunctionResult } from '../types.js';
2
+ export declare function deriveKey(parameters: ChallengeParameters, salt: Uint8Array, password: Uint8Array): Promise<DeriveKeyFunctionResult>;
@@ -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,2 @@
1
+ import type { ChallengeParameters, DeriveKeyFunctionResult } from '../types.js';
2
+ export declare function deriveKey(parameters: ChallengeParameters, salt: Uint8Array, password: Uint8Array): Promise<DeriveKeyFunctionResult>;
@@ -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,2 @@
1
+ import type { ChallengeParameters, DeriveKeyFunctionResult } from '../../types.js';
2
+ export declare function deriveKey(parameters: ChallengeParameters, salt: Uint8Array, password: Uint8Array): Promise<DeriveKeyFunctionResult>;
@@ -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,2 @@
1
+ import type { ChallengeParameters, DeriveKeyFunctionResult } from '../../types.js';
2
+ export declare function deriveKey(parameters: ChallengeParameters, salt: Uint8Array, password: Uint8Array): Promise<DeriveKeyFunctionResult>;
@@ -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,7 @@
1
+ export declare class CappedMap<K = unknown, V = unknown> extends Map {
2
+ readonly maxSize: number;
3
+ constructor(options: {
4
+ maxSize: number;
5
+ });
6
+ set(key: K, value: V): this;
7
+ }
@@ -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;