altcha-lib 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -86,6 +86,30 @@ Parameters:
86
86
 
87
87
  Returns: `Promise<boolean>`
88
88
 
89
+ ### `verifyServerSignature(payload, hmacKey)`
90
+
91
+ Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.
92
+
93
+ Parameters:
94
+
95
+ - `payload: string | ServerSignaturePayload`
96
+ - `hmacKey: string`
97
+
98
+ Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`
99
+
100
+ ### `verifyFieldsHash(formData, fields, fieldsHash, algorithm?)`
101
+
102
+ Verifies the hash of form fields returned by the Spam Filter.
103
+
104
+ Parameters:
105
+
106
+ - `formData: FormData | Record<string, unknown>`
107
+ - `fields: string[]`
108
+ - `fieldsHash: string`
109
+ - `algorithm: Algorithm = 'SHA-256'`
110
+
111
+ Returns: `Promise<boolean>`
112
+
89
113
  ### `solveChallenge(challenge, salt, algorithm?, max?, start?)`
90
114
 
91
115
  Finds a solution to the given challenge.
@@ -100,17 +124,6 @@ Parameters:
100
124
 
101
125
  Returns: `{ controller: AbortController, promise: Promise<Solution | null> }`
102
126
 
103
- ### `verifyServerSignature(payload, hmacKey)`
104
-
105
- Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.
106
-
107
- Parameters:
108
-
109
- - `payload: string | ServerSignaturePayload`
110
- - `hmacKey: string`
111
-
112
- Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`
113
-
114
127
  ### `solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm?, max?, start?)`
115
128
 
116
129
  Finds a solution to the given challenge with [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) running concurrently.
@@ -1,23 +1,83 @@
1
- import type { Challenge, ChallengeOptions, Payload, ServerSignaturePayload, ServerSignatureVerificationData, Solution } from './types.js';
1
+ import type { Algorithm, Challenge, ChallengeOptions, Payload, ServerSignaturePayload, ServerSignatureVerificationData, Solution } from './types.js';
2
+ /**
3
+ * Creates a challenge for the client to solve.
4
+ *
5
+ * @param {ChallengeOptions} options - Options for creating the challenge.
6
+ * @returns {Promise<Challenge>} The created challenge.
7
+ */
2
8
  export declare function createChallenge(options: ChallengeOptions): Promise<Challenge>;
9
+ /**
10
+ * Extracts parameters from the payload.
11
+ *
12
+ * @param {string | Payload | Challenge} payload - The payload from which to extract parameters.
13
+ * @returns {Record<string, string>} The extracted parameters.
14
+ */
3
15
  export declare function extractParams(payload: string | Payload | Challenge): {
4
16
  [k: string]: string;
5
17
  };
18
+ /**
19
+ * Verifies the solution provided by the client.
20
+ *
21
+ * @param {string | Payload} payload - The payload to verify.
22
+ * @param {string} hmacKey - The HMAC key used for verification.
23
+ * @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.
24
+ * @returns {Promise<boolean>} Whether the solution is valid.
25
+ */
6
26
  export declare function verifySolution(payload: string | Payload, hmacKey: string, checkExpires?: boolean): Promise<boolean>;
27
+ /**
28
+ * Verifies the hash of form fields.
29
+ *
30
+ * @param {FormData | Record<string, unknown>} formData - The form data to verify.
31
+ * @param {string[]} fields - The fields to include in the hash.
32
+ * @param {string} fieldsHash - The expected hash of the fields.
33
+ * @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.
34
+ * @returns {Promise<boolean>} Whether the fields hash is valid.
35
+ */
36
+ export declare function verifyFieldsHash(formData: FormData | Record<string, unknown>, fields: string[], fieldsHash: string, algorithm?: Algorithm): Promise<boolean>;
37
+ /**
38
+ * Verifies the server's signature.
39
+ *
40
+ * @param {string | ServerSignaturePayload} payload - The payload to verify.
41
+ * @param {string} hmacKey - The HMAC key used for verification.
42
+ * @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.
43
+ */
7
44
  export declare function verifyServerSignature(payload: string | ServerSignaturePayload, hmacKey: string): Promise<{
8
45
  verificationData: ServerSignatureVerificationData | null;
9
- verified: boolean | null;
46
+ verified: boolean;
10
47
  }>;
48
+ /**
49
+ * Solves a challenge by brute force.
50
+ *
51
+ * @param {string} challenge - The challenge to solve.
52
+ * @param {string} salt - The salt used in the challenge.
53
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
54
+ * @param {number} [max=1e6] - The maximum number to try.
55
+ * @param {number} [start=0] - The starting number.
56
+ * @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.
57
+ */
11
58
  export declare function solveChallenge(challenge: string, salt: string, algorithm?: string, max?: number, start?: number): {
12
59
  promise: Promise<Solution | null>;
13
60
  controller: AbortController;
14
61
  };
62
+ /**
63
+ * Solves a challenge using web workers for parallel computation.
64
+ *
65
+ * @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.
66
+ * @param {number} concurrency - The number of workers to use.
67
+ * @param {string} challenge - The challenge to solve.
68
+ * @param {string} salt - The salt used in the challenge.
69
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
70
+ * @param {number} [max=1e6] - The maximum number to try.
71
+ * @param {number} [startNumber=0] - The starting number.
72
+ * @returns {Promise<Solution | null>} The solution, or null if not found.
73
+ */
15
74
  export declare function solveChallengeWorkers(workerScript: string | URL | (() => Worker), concurrency: number, challenge: string, salt: string, algorithm?: string, max?: number, startNumber?: number): Promise<Solution | null>;
16
75
  declare const _default: {
17
76
  createChallenge: typeof createChallenge;
18
77
  extractParams: typeof extractParams;
19
78
  solveChallenge: typeof solveChallenge;
20
79
  solveChallengeWorkers: typeof solveChallengeWorkers;
80
+ verifyFieldsHash: typeof verifyFieldsHash;
21
81
  verifyServerSignature: typeof verifyServerSignature;
22
82
  verifySolution: typeof verifySolution;
23
83
  };
package/cjs/dist/index.js CHANGED
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.solveChallengeWorkers = exports.solveChallenge = exports.verifyServerSignature = exports.verifySolution = exports.extractParams = exports.createChallenge = void 0;
3
+ exports.solveChallengeWorkers = exports.solveChallenge = exports.verifyServerSignature = exports.verifyFieldsHash = exports.verifySolution = exports.extractParams = exports.createChallenge = void 0;
4
4
  const helpers_js_1 = require("./helpers.js");
5
5
  const DEFAULT_MAX_NUMBER = 1e6;
6
6
  const DEFAULT_SALT_LEN = 12;
7
7
  const DEFAULT_ALG = 'SHA-256';
8
+ /**
9
+ * Creates a challenge for the client to solve.
10
+ *
11
+ * @param {ChallengeOptions} options - Options for creating the challenge.
12
+ * @returns {Promise<Challenge>} The created challenge.
13
+ */
8
14
  async function createChallenge(options) {
9
15
  const algorithm = options.algorithm || DEFAULT_ALG;
10
16
  const maxnumber = options.maxnumber || options.maxNumber || DEFAULT_MAX_NUMBER;
@@ -29,6 +35,12 @@ async function createChallenge(options) {
29
35
  };
30
36
  }
31
37
  exports.createChallenge = createChallenge;
38
+ /**
39
+ * Extracts parameters from the payload.
40
+ *
41
+ * @param {string | Payload | Challenge} payload - The payload from which to extract parameters.
42
+ * @returns {Record<string, string>} The extracted parameters.
43
+ */
32
44
  function extractParams(payload) {
33
45
  if (typeof payload === 'string') {
34
46
  payload = JSON.parse(atob(payload));
@@ -36,9 +48,22 @@ function extractParams(payload) {
36
48
  return Object.fromEntries(new URLSearchParams(payload.salt.split('?')?.[1] || ''));
37
49
  }
38
50
  exports.extractParams = extractParams;
51
+ /**
52
+ * Verifies the solution provided by the client.
53
+ *
54
+ * @param {string | Payload} payload - The payload to verify.
55
+ * @param {string} hmacKey - The HMAC key used for verification.
56
+ * @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.
57
+ * @returns {Promise<boolean>} Whether the solution is valid.
58
+ */
39
59
  async function verifySolution(payload, hmacKey, checkExpires = true) {
40
60
  if (typeof payload === 'string') {
41
- payload = JSON.parse(atob(payload));
61
+ try {
62
+ payload = JSON.parse(atob(payload));
63
+ }
64
+ catch {
65
+ return false;
66
+ }
42
67
  }
43
68
  const params = extractParams(payload);
44
69
  const expires = params.expires || params.expire;
@@ -58,9 +83,42 @@ async function verifySolution(payload, hmacKey, checkExpires = true) {
58
83
  check.signature === payload.signature);
59
84
  }
60
85
  exports.verifySolution = verifySolution;
86
+ /**
87
+ * Verifies the hash of form fields.
88
+ *
89
+ * @param {FormData | Record<string, unknown>} formData - The form data to verify.
90
+ * @param {string[]} fields - The fields to include in the hash.
91
+ * @param {string} fieldsHash - The expected hash of the fields.
92
+ * @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.
93
+ * @returns {Promise<boolean>} Whether the fields hash is valid.
94
+ */
95
+ async function verifyFieldsHash(formData, fields, fieldsHash, algorithm = DEFAULT_ALG) {
96
+ const data = formData instanceof FormData ? Object.fromEntries(formData) : formData;
97
+ const lines = [];
98
+ for (const field of fields) {
99
+ lines.push(String(data[field] || ''));
100
+ }
101
+ return (await (0, helpers_js_1.hashHex)(algorithm, lines.join('\n'))) === fieldsHash;
102
+ }
103
+ exports.verifyFieldsHash = verifyFieldsHash;
104
+ /**
105
+ * Verifies the server's signature.
106
+ *
107
+ * @param {string | ServerSignaturePayload} payload - The payload to verify.
108
+ * @param {string} hmacKey - The HMAC key used for verification.
109
+ * @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.
110
+ */
61
111
  async function verifyServerSignature(payload, hmacKey) {
62
112
  if (typeof payload === 'string') {
63
- payload = JSON.parse(atob(payload));
113
+ try {
114
+ payload = JSON.parse(atob(payload));
115
+ }
116
+ catch {
117
+ return {
118
+ verificationData: null,
119
+ verified: false,
120
+ };
121
+ }
64
122
  }
65
123
  const signature = await (0, helpers_js_1.hmacHex)(payload.algorithm, await (0, helpers_js_1.hash)(payload.algorithm, payload.verificationData), hmacKey);
66
124
  let verificationData = null;
@@ -84,13 +142,22 @@ async function verifyServerSignature(payload, hmacKey) {
84
142
  return {
85
143
  verificationData,
86
144
  verified: payload.verified === true &&
87
- verificationData &&
88
- verificationData.verified === true &&
145
+ verificationData?.verified === true &&
89
146
  verificationData.expire > Math.floor(Date.now() / 1000) &&
90
147
  payload.signature === signature,
91
148
  };
92
149
  }
93
150
  exports.verifyServerSignature = verifyServerSignature;
151
+ /**
152
+ * Solves a challenge by brute force.
153
+ *
154
+ * @param {string} challenge - The challenge to solve.
155
+ * @param {string} salt - The salt used in the challenge.
156
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
157
+ * @param {number} [max=1e6] - The maximum number to try.
158
+ * @param {number} [start=0] - The starting number.
159
+ * @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.
160
+ */
94
161
  function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start = 0) {
95
162
  const controller = new AbortController();
96
163
  const startTime = Date.now();
@@ -115,14 +182,21 @@ function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start
115
182
  };
116
183
  }
117
184
  exports.solveChallenge = solveChallenge;
185
+ /**
186
+ * Solves a challenge using web workers for parallel computation.
187
+ *
188
+ * @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.
189
+ * @param {number} concurrency - The number of workers to use.
190
+ * @param {string} challenge - The challenge to solve.
191
+ * @param {string} salt - The salt used in the challenge.
192
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
193
+ * @param {number} [max=1e6] - The maximum number to try.
194
+ * @param {number} [startNumber=0] - The starting number.
195
+ * @returns {Promise<Solution | null>} The solution, or null if not found.
196
+ */
118
197
  async function solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm = 'SHA-256', max = 1e6, startNumber = 0) {
119
198
  const workers = [];
120
- if (concurrency < 1) {
121
- throw new Error('Wrong number of workers configured.');
122
- }
123
- if (concurrency > 16) {
124
- throw new Error('Too many workers. Max. 16 allowed workers.');
125
- }
199
+ concurrency = Math.min(1, Math.max(16, concurrency));
126
200
  for (let i = 0; i < concurrency; i++) {
127
201
  if (typeof workerScript === 'function') {
128
202
  workers.push(workerScript());
@@ -170,6 +244,7 @@ exports.default = {
170
244
  extractParams,
171
245
  solveChallenge,
172
246
  solveChallengeWorkers,
247
+ verifyFieldsHash,
173
248
  verifyServerSignature,
174
249
  verifySolution,
175
250
  };
@@ -1,4 +1,5 @@
1
1
  export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
2
+ export type Classification = 'BAD' | 'GOOD' | 'NEUTRAL';
2
3
  export interface Challenge {
3
4
  algorithm: Algorithm;
4
5
  challenge: string;
@@ -31,11 +32,14 @@ export interface ServerSignaturePayload {
31
32
  verified: boolean;
32
33
  }
33
34
  export interface ServerSignatureVerificationData {
34
- classification?: string;
35
+ classification?: Classification;
36
+ country?: string;
37
+ detectedLanguage?: string;
35
38
  email?: string;
36
39
  expire: number;
37
40
  fields?: string[];
38
41
  fieldsHash?: string;
42
+ ipAddress?: string;
39
43
  reasons?: string[];
40
44
  score?: number;
41
45
  time: number;
package/dist/index.d.ts CHANGED
@@ -1,23 +1,83 @@
1
- import type { Challenge, ChallengeOptions, Payload, ServerSignaturePayload, ServerSignatureVerificationData, Solution } from './types.js';
1
+ import type { Algorithm, Challenge, ChallengeOptions, Payload, ServerSignaturePayload, ServerSignatureVerificationData, Solution } from './types.js';
2
+ /**
3
+ * Creates a challenge for the client to solve.
4
+ *
5
+ * @param {ChallengeOptions} options - Options for creating the challenge.
6
+ * @returns {Promise<Challenge>} The created challenge.
7
+ */
2
8
  export declare function createChallenge(options: ChallengeOptions): Promise<Challenge>;
9
+ /**
10
+ * Extracts parameters from the payload.
11
+ *
12
+ * @param {string | Payload | Challenge} payload - The payload from which to extract parameters.
13
+ * @returns {Record<string, string>} The extracted parameters.
14
+ */
3
15
  export declare function extractParams(payload: string | Payload | Challenge): {
4
16
  [k: string]: string;
5
17
  };
18
+ /**
19
+ * Verifies the solution provided by the client.
20
+ *
21
+ * @param {string | Payload} payload - The payload to verify.
22
+ * @param {string} hmacKey - The HMAC key used for verification.
23
+ * @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.
24
+ * @returns {Promise<boolean>} Whether the solution is valid.
25
+ */
6
26
  export declare function verifySolution(payload: string | Payload, hmacKey: string, checkExpires?: boolean): Promise<boolean>;
27
+ /**
28
+ * Verifies the hash of form fields.
29
+ *
30
+ * @param {FormData | Record<string, unknown>} formData - The form data to verify.
31
+ * @param {string[]} fields - The fields to include in the hash.
32
+ * @param {string} fieldsHash - The expected hash of the fields.
33
+ * @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.
34
+ * @returns {Promise<boolean>} Whether the fields hash is valid.
35
+ */
36
+ export declare function verifyFieldsHash(formData: FormData | Record<string, unknown>, fields: string[], fieldsHash: string, algorithm?: Algorithm): Promise<boolean>;
37
+ /**
38
+ * Verifies the server's signature.
39
+ *
40
+ * @param {string | ServerSignaturePayload} payload - The payload to verify.
41
+ * @param {string} hmacKey - The HMAC key used for verification.
42
+ * @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.
43
+ */
7
44
  export declare function verifyServerSignature(payload: string | ServerSignaturePayload, hmacKey: string): Promise<{
8
45
  verificationData: ServerSignatureVerificationData | null;
9
- verified: boolean | null;
46
+ verified: boolean;
10
47
  }>;
48
+ /**
49
+ * Solves a challenge by brute force.
50
+ *
51
+ * @param {string} challenge - The challenge to solve.
52
+ * @param {string} salt - The salt used in the challenge.
53
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
54
+ * @param {number} [max=1e6] - The maximum number to try.
55
+ * @param {number} [start=0] - The starting number.
56
+ * @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.
57
+ */
11
58
  export declare function solveChallenge(challenge: string, salt: string, algorithm?: string, max?: number, start?: number): {
12
59
  promise: Promise<Solution | null>;
13
60
  controller: AbortController;
14
61
  };
62
+ /**
63
+ * Solves a challenge using web workers for parallel computation.
64
+ *
65
+ * @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.
66
+ * @param {number} concurrency - The number of workers to use.
67
+ * @param {string} challenge - The challenge to solve.
68
+ * @param {string} salt - The salt used in the challenge.
69
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
70
+ * @param {number} [max=1e6] - The maximum number to try.
71
+ * @param {number} [startNumber=0] - The starting number.
72
+ * @returns {Promise<Solution | null>} The solution, or null if not found.
73
+ */
15
74
  export declare function solveChallengeWorkers(workerScript: string | URL | (() => Worker), concurrency: number, challenge: string, salt: string, algorithm?: string, max?: number, startNumber?: number): Promise<Solution | null>;
16
75
  declare const _default: {
17
76
  createChallenge: typeof createChallenge;
18
77
  extractParams: typeof extractParams;
19
78
  solveChallenge: typeof solveChallenge;
20
79
  solveChallengeWorkers: typeof solveChallengeWorkers;
80
+ verifyFieldsHash: typeof verifyFieldsHash;
21
81
  verifyServerSignature: typeof verifyServerSignature;
22
82
  verifySolution: typeof verifySolution;
23
83
  };
package/dist/index.js CHANGED
@@ -2,6 +2,12 @@ import { ab2hex, hash, hashHex, hmacHex, randomBytes, randomInt, } from './helpe
2
2
  const DEFAULT_MAX_NUMBER = 1e6;
3
3
  const DEFAULT_SALT_LEN = 12;
4
4
  const DEFAULT_ALG = 'SHA-256';
5
+ /**
6
+ * Creates a challenge for the client to solve.
7
+ *
8
+ * @param {ChallengeOptions} options - Options for creating the challenge.
9
+ * @returns {Promise<Challenge>} The created challenge.
10
+ */
5
11
  export async function createChallenge(options) {
6
12
  const algorithm = options.algorithm || DEFAULT_ALG;
7
13
  const maxnumber = options.maxnumber || options.maxNumber || DEFAULT_MAX_NUMBER;
@@ -25,15 +31,34 @@ export async function createChallenge(options) {
25
31
  signature: await hmacHex(algorithm, challenge, options.hmacKey),
26
32
  };
27
33
  }
34
+ /**
35
+ * Extracts parameters from the payload.
36
+ *
37
+ * @param {string | Payload | Challenge} payload - The payload from which to extract parameters.
38
+ * @returns {Record<string, string>} The extracted parameters.
39
+ */
28
40
  export function extractParams(payload) {
29
41
  if (typeof payload === 'string') {
30
42
  payload = JSON.parse(atob(payload));
31
43
  }
32
44
  return Object.fromEntries(new URLSearchParams(payload.salt.split('?')?.[1] || ''));
33
45
  }
46
+ /**
47
+ * Verifies the solution provided by the client.
48
+ *
49
+ * @param {string | Payload} payload - The payload to verify.
50
+ * @param {string} hmacKey - The HMAC key used for verification.
51
+ * @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.
52
+ * @returns {Promise<boolean>} Whether the solution is valid.
53
+ */
34
54
  export async function verifySolution(payload, hmacKey, checkExpires = true) {
35
55
  if (typeof payload === 'string') {
36
- payload = JSON.parse(atob(payload));
56
+ try {
57
+ payload = JSON.parse(atob(payload));
58
+ }
59
+ catch {
60
+ return false;
61
+ }
37
62
  }
38
63
  const params = extractParams(payload);
39
64
  const expires = params.expires || params.expire;
@@ -52,9 +77,41 @@ export async function verifySolution(payload, hmacKey, checkExpires = true) {
52
77
  return (check.challenge === payload.challenge &&
53
78
  check.signature === payload.signature);
54
79
  }
80
+ /**
81
+ * Verifies the hash of form fields.
82
+ *
83
+ * @param {FormData | Record<string, unknown>} formData - The form data to verify.
84
+ * @param {string[]} fields - The fields to include in the hash.
85
+ * @param {string} fieldsHash - The expected hash of the fields.
86
+ * @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.
87
+ * @returns {Promise<boolean>} Whether the fields hash is valid.
88
+ */
89
+ export async function verifyFieldsHash(formData, fields, fieldsHash, algorithm = DEFAULT_ALG) {
90
+ const data = formData instanceof FormData ? Object.fromEntries(formData) : formData;
91
+ const lines = [];
92
+ for (const field of fields) {
93
+ lines.push(String(data[field] || ''));
94
+ }
95
+ return (await hashHex(algorithm, lines.join('\n'))) === fieldsHash;
96
+ }
97
+ /**
98
+ * Verifies the server's signature.
99
+ *
100
+ * @param {string | ServerSignaturePayload} payload - The payload to verify.
101
+ * @param {string} hmacKey - The HMAC key used for verification.
102
+ * @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.
103
+ */
55
104
  export async function verifyServerSignature(payload, hmacKey) {
56
105
  if (typeof payload === 'string') {
57
- payload = JSON.parse(atob(payload));
106
+ try {
107
+ payload = JSON.parse(atob(payload));
108
+ }
109
+ catch {
110
+ return {
111
+ verificationData: null,
112
+ verified: false,
113
+ };
114
+ }
58
115
  }
59
116
  const signature = await hmacHex(payload.algorithm, await hash(payload.algorithm, payload.verificationData), hmacKey);
60
117
  let verificationData = null;
@@ -78,12 +135,21 @@ export async function verifyServerSignature(payload, hmacKey) {
78
135
  return {
79
136
  verificationData,
80
137
  verified: payload.verified === true &&
81
- verificationData &&
82
- verificationData.verified === true &&
138
+ verificationData?.verified === true &&
83
139
  verificationData.expire > Math.floor(Date.now() / 1000) &&
84
140
  payload.signature === signature,
85
141
  };
86
142
  }
143
+ /**
144
+ * Solves a challenge by brute force.
145
+ *
146
+ * @param {string} challenge - The challenge to solve.
147
+ * @param {string} salt - The salt used in the challenge.
148
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
149
+ * @param {number} [max=1e6] - The maximum number to try.
150
+ * @param {number} [start=0] - The starting number.
151
+ * @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.
152
+ */
87
153
  export function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start = 0) {
88
154
  const controller = new AbortController();
89
155
  const startTime = Date.now();
@@ -107,14 +173,21 @@ export function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6
107
173
  controller,
108
174
  };
109
175
  }
176
+ /**
177
+ * Solves a challenge using web workers for parallel computation.
178
+ *
179
+ * @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.
180
+ * @param {number} concurrency - The number of workers to use.
181
+ * @param {string} challenge - The challenge to solve.
182
+ * @param {string} salt - The salt used in the challenge.
183
+ * @param {string} [algorithm='SHA-256'] - The hash algorithm used.
184
+ * @param {number} [max=1e6] - The maximum number to try.
185
+ * @param {number} [startNumber=0] - The starting number.
186
+ * @returns {Promise<Solution | null>} The solution, or null if not found.
187
+ */
110
188
  export async function solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm = 'SHA-256', max = 1e6, startNumber = 0) {
111
189
  const workers = [];
112
- if (concurrency < 1) {
113
- throw new Error('Wrong number of workers configured.');
114
- }
115
- if (concurrency > 16) {
116
- throw new Error('Too many workers. Max. 16 allowed workers.');
117
- }
190
+ concurrency = Math.min(1, Math.max(16, concurrency));
118
191
  for (let i = 0; i < concurrency; i++) {
119
192
  if (typeof workerScript === 'function') {
120
193
  workers.push(workerScript());
@@ -161,6 +234,7 @@ export default {
161
234
  extractParams,
162
235
  solveChallenge,
163
236
  solveChallengeWorkers,
237
+ verifyFieldsHash,
164
238
  verifyServerSignature,
165
239
  verifySolution,
166
240
  };
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
2
+ export type Classification = 'BAD' | 'GOOD' | 'NEUTRAL';
2
3
  export interface Challenge {
3
4
  algorithm: Algorithm;
4
5
  challenge: string;
@@ -31,11 +32,14 @@ export interface ServerSignaturePayload {
31
32
  verified: boolean;
32
33
  }
33
34
  export interface ServerSignatureVerificationData {
34
- classification?: string;
35
+ classification?: Classification;
36
+ country?: string;
37
+ detectedLanguage?: string;
35
38
  email?: string;
36
39
  expire: number;
37
40
  fields?: string[];
38
41
  fieldsHash?: string;
42
+ ipAddress?: string;
39
43
  reasons?: string[];
40
44
  score?: number;
41
45
  time: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "altcha-lib",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A library for creating and verifying ALTCHA challenges for Node.js, Bun and Deno.",
5
5
  "author": "Daniel Regeci",
6
6
  "license": "MIT",