Skip to content

Commit

Permalink
add verifyFieldsHash
Browse files Browse the repository at this point in the history
  • Loading branch information
ovx committed Jul 24, 2024
1 parent 42bf72f commit fd2817c
Show file tree
Hide file tree
Showing 15 changed files with 664 additions and 61 deletions.
35 changes: 24 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ Parameters:

Returns: `Promise<boolean>`

### `verifyServerSignature(payload, hmacKey)`

Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.

Parameters:

- `payload: string | ServerSignaturePayload`
- `hmacKey: string`

Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`

### `verifyFieldsHash(formData, fields, fieldsHash, algorithm?)`

Verifies the hash of form fields returned by the Spam Filter.

Parameters:

- `formData: FormData | Record<string, unknown>`
- `fields: string[]`
- `fieldsHash: string`
- `algorithm: Algorithm = 'SHA-256'`

Returns: `Promise<boolean>`

### `solveChallenge(challenge, salt, algorithm?, max?, start?)`

Finds a solution to the given challenge.
Expand All @@ -100,17 +124,6 @@ Parameters:

Returns: `{ controller: AbortController, promise: Promise<Solution | null> }`

### `verifyServerSignature(payload, hmacKey)`

Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.

Parameters:

- `payload: string | ServerSignaturePayload`
- `hmacKey: string`

Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`

### `solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm?, max?, start?)`

Finds a solution to the given challenge with [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) running concurrently.
Expand Down
64 changes: 62 additions & 2 deletions cjs/dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,83 @@
import type { Challenge, ChallengeOptions, Payload, ServerSignaturePayload, ServerSignatureVerificationData, Solution } from './types.js';
import type { Algorithm, Challenge, ChallengeOptions, Payload, ServerSignaturePayload, ServerSignatureVerificationData, Solution } from './types.js';
/**
* Creates a challenge for the client to solve.
*
* @param {ChallengeOptions} options - Options for creating the challenge.
* @returns {Promise<Challenge>} The created challenge.
*/
export declare function createChallenge(options: ChallengeOptions): Promise<Challenge>;
/**
* Extracts parameters from the payload.
*
* @param {string | Payload | Challenge} payload - The payload from which to extract parameters.
* @returns {Record<string, string>} The extracted parameters.
*/
export declare function extractParams(payload: string | Payload | Challenge): {
[k: string]: string;
};
/**
* Verifies the solution provided by the client.
*
* @param {string | Payload} payload - The payload to verify.
* @param {string} hmacKey - The HMAC key used for verification.
* @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.
* @returns {Promise<boolean>} Whether the solution is valid.
*/
export declare function verifySolution(payload: string | Payload, hmacKey: string, checkExpires?: boolean): Promise<boolean>;
/**
* Verifies the hash of form fields.
*
* @param {FormData | Record<string, unknown>} formData - The form data to verify.
* @param {string[]} fields - The fields to include in the hash.
* @param {string} fieldsHash - The expected hash of the fields.
* @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.
* @returns {Promise<boolean>} Whether the fields hash is valid.
*/
export declare function verifyFieldsHash(formData: FormData | Record<string, unknown>, fields: string[], fieldsHash: string, algorithm?: Algorithm): Promise<boolean>;
/**
* Verifies the server's signature.
*
* @param {string | ServerSignaturePayload} payload - The payload to verify.
* @param {string} hmacKey - The HMAC key used for verification.
* @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.
*/
export declare function verifyServerSignature(payload: string | ServerSignaturePayload, hmacKey: string): Promise<{
verificationData: ServerSignatureVerificationData | null;
verified: boolean | null;
verified: boolean;
}>;
/**
* Solves a challenge by brute force.
*
* @param {string} challenge - The challenge to solve.
* @param {string} salt - The salt used in the challenge.
* @param {string} [algorithm='SHA-256'] - The hash algorithm used.
* @param {number} [max=1e6] - The maximum number to try.
* @param {number} [start=0] - The starting number.
* @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.
*/
export declare function solveChallenge(challenge: string, salt: string, algorithm?: string, max?: number, start?: number): {
promise: Promise<Solution | null>;
controller: AbortController;
};
/**
* Solves a challenge using web workers for parallel computation.
*
* @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.
* @param {number} concurrency - The number of workers to use.
* @param {string} challenge - The challenge to solve.
* @param {string} salt - The salt used in the challenge.
* @param {string} [algorithm='SHA-256'] - The hash algorithm used.
* @param {number} [max=1e6] - The maximum number to try.
* @param {number} [startNumber=0] - The starting number.
* @returns {Promise<Solution | null>} The solution, or null if not found.
*/
export declare function solveChallengeWorkers(workerScript: string | URL | (() => Worker), concurrency: number, challenge: string, salt: string, algorithm?: string, max?: number, startNumber?: number): Promise<Solution | null>;
declare const _default: {
createChallenge: typeof createChallenge;
extractParams: typeof extractParams;
solveChallenge: typeof solveChallenge;
solveChallengeWorkers: typeof solveChallengeWorkers;
verifyFieldsHash: typeof verifyFieldsHash;
verifyServerSignature: typeof verifyServerSignature;
verifySolution: typeof verifySolution;
};
Expand Down
90 changes: 85 additions & 5 deletions cjs/dist/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.solveChallengeWorkers = exports.solveChallenge = exports.verifyServerSignature = exports.verifySolution = exports.extractParams = exports.createChallenge = void 0;
exports.solveChallengeWorkers = exports.solveChallenge = exports.verifyServerSignature = exports.verifyFieldsHash = exports.verifySolution = exports.extractParams = exports.createChallenge = void 0;
const helpers_js_1 = require("./helpers.js");
const DEFAULT_MAX_NUMBER = 1e6;
const DEFAULT_SALT_LEN = 12;
const DEFAULT_ALG = 'SHA-256';
/**
* Creates a challenge for the client to solve.
*
* @param {ChallengeOptions} options - Options for creating the challenge.
* @returns {Promise<Challenge>} The created challenge.
*/
async function createChallenge(options) {
const algorithm = options.algorithm || DEFAULT_ALG;
const maxnumber = options.maxnumber || options.maxNumber || DEFAULT_MAX_NUMBER;
Expand All @@ -29,16 +35,35 @@ async function createChallenge(options) {
};
}
exports.createChallenge = createChallenge;
/**
* Extracts parameters from the payload.
*
* @param {string | Payload | Challenge} payload - The payload from which to extract parameters.
* @returns {Record<string, string>} The extracted parameters.
*/
function extractParams(payload) {
if (typeof payload === 'string') {
payload = JSON.parse(atob(payload));
}
return Object.fromEntries(new URLSearchParams(payload.salt.split('?')?.[1] || ''));
}
exports.extractParams = extractParams;
/**
* Verifies the solution provided by the client.
*
* @param {string | Payload} payload - The payload to verify.
* @param {string} hmacKey - The HMAC key used for verification.
* @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.
* @returns {Promise<boolean>} Whether the solution is valid.
*/
async function verifySolution(payload, hmacKey, checkExpires = true) {
if (typeof payload === 'string') {
payload = JSON.parse(atob(payload));
try {
payload = JSON.parse(atob(payload));
}
catch {
return false;
}
}
const params = extractParams(payload);
const expires = params.expires || params.expire;
Expand All @@ -58,9 +83,42 @@ async function verifySolution(payload, hmacKey, checkExpires = true) {
check.signature === payload.signature);
}
exports.verifySolution = verifySolution;
/**
* Verifies the hash of form fields.
*
* @param {FormData | Record<string, unknown>} formData - The form data to verify.
* @param {string[]} fields - The fields to include in the hash.
* @param {string} fieldsHash - The expected hash of the fields.
* @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.
* @returns {Promise<boolean>} Whether the fields hash is valid.
*/
async function verifyFieldsHash(formData, fields, fieldsHash, algorithm = DEFAULT_ALG) {
const data = formData instanceof FormData ? Object.fromEntries(formData) : formData;
const lines = [];
for (const field of fields) {
lines.push(String(data[field] || ''));
}
return (await (0, helpers_js_1.hashHex)(algorithm, lines.join('\n'))) === fieldsHash;
}
exports.verifyFieldsHash = verifyFieldsHash;
/**
* Verifies the server's signature.
*
* @param {string | ServerSignaturePayload} payload - The payload to verify.
* @param {string} hmacKey - The HMAC key used for verification.
* @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.
*/
async function verifyServerSignature(payload, hmacKey) {
if (typeof payload === 'string') {
payload = JSON.parse(atob(payload));
try {
payload = JSON.parse(atob(payload));
}
catch {
return {
verificationData: null,
verified: false,
};
}
}
const signature = await (0, helpers_js_1.hmacHex)(payload.algorithm, await (0, helpers_js_1.hash)(payload.algorithm, payload.verificationData), hmacKey);
let verificationData = null;
Expand All @@ -84,13 +142,22 @@ async function verifyServerSignature(payload, hmacKey) {
return {
verificationData,
verified: payload.verified === true &&
verificationData &&
verificationData.verified === true &&
verificationData?.verified === true &&
verificationData.expire > Math.floor(Date.now() / 1000) &&
payload.signature === signature,
};
}
exports.verifyServerSignature = verifyServerSignature;
/**
* Solves a challenge by brute force.
*
* @param {string} challenge - The challenge to solve.
* @param {string} salt - The salt used in the challenge.
* @param {string} [algorithm='SHA-256'] - The hash algorithm used.
* @param {number} [max=1e6] - The maximum number to try.
* @param {number} [start=0] - The starting number.
* @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.
*/
function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start = 0) {
const controller = new AbortController();
const startTime = Date.now();
Expand All @@ -115,6 +182,18 @@ function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start
};
}
exports.solveChallenge = solveChallenge;
/**
* Solves a challenge using web workers for parallel computation.
*
* @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.
* @param {number} concurrency - The number of workers to use.
* @param {string} challenge - The challenge to solve.
* @param {string} salt - The salt used in the challenge.
* @param {string} [algorithm='SHA-256'] - The hash algorithm used.
* @param {number} [max=1e6] - The maximum number to try.
* @param {number} [startNumber=0] - The starting number.
* @returns {Promise<Solution | null>} The solution, or null if not found.
*/
async function solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm = 'SHA-256', max = 1e6, startNumber = 0) {
const workers = [];
concurrency = Math.min(1, Math.max(16, concurrency));
Expand Down Expand Up @@ -165,6 +244,7 @@ exports.default = {
extractParams,
solveChallenge,
solveChallengeWorkers,
verifyFieldsHash,
verifyServerSignature,
verifySolution,
};
6 changes: 5 additions & 1 deletion cjs/dist/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
export type Classification = 'BAD' | 'GOOD' | 'NEUTRAL';
export interface Challenge {
algorithm: Algorithm;
challenge: string;
Expand Down Expand Up @@ -31,11 +32,14 @@ export interface ServerSignaturePayload {
verified: boolean;
}
export interface ServerSignatureVerificationData {
classification?: string;
classification?: Classification;
country?: string;
detectedLanguage?: string;
email?: string;
expire: number;
fields?: string[];
fieldsHash?: string;
ipAddress?: string;
reasons?: string[];
score?: number;
time: number;
Expand Down
35 changes: 24 additions & 11 deletions deno_dist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ Parameters:

Returns: `Promise<boolean>`

### `verifyServerSignature(payload, hmacKey)`

Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.

Parameters:

- `payload: string | ServerSignaturePayload`
- `hmacKey: string`

Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`

### `verifyFieldsHash(formData, fields, fieldsHash, algorithm?)`

Verifies the hash of form fields returned by the Spam Filter.

Parameters:

- `formData: FormData | Record<string, unknown>`
- `fields: string[]`
- `fieldsHash: string`
- `algorithm: Algorithm = 'SHA-256'`

Returns: `Promise<boolean>`

### `solveChallenge(challenge, salt, algorithm?, max?, start?)`

Finds a solution to the given challenge.
Expand All @@ -100,17 +124,6 @@ Parameters:

Returns: `{ controller: AbortController, promise: Promise<Solution | null> }`

### `verifyServerSignature(payload, hmacKey)`

Verifies the server signature returned by the API. The payload can be a Base64-encoded JSON payload or an object.

Parameters:

- `payload: string | ServerSignaturePayload`
- `hmacKey: string`

Returns: `Promise<{ verificationData: ServerSignatureVerificationData | null, verified: boolean }>`

### `solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm?, max?, start?)`

Finds a solution to the given challenge with [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) running concurrently.
Expand Down
Loading

0 comments on commit fd2817c

Please sign in to comment.