altcha-lib 0.1.0 → 0.1.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/README.md +2 -0
- package/cjs/dist/helpers.d.ts +6 -0
- package/cjs/dist/helpers.js +39 -0
- package/cjs/dist/index.d.ts +3 -0
- package/cjs/dist/index.js +36 -0
- package/cjs/dist/types.d.ts +22 -0
- package/cjs/dist/types.js +2 -0
- package/cjs/package.json +3 -0
- package/dist/helpers.js +2 -2
- package/dist/index.js +6 -3
- package/dist/types.d.ts +2 -0
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -37,8 +37,10 @@ Parameters:
|
|
|
37
37
|
- `options: ChallengeOptions`:
|
|
38
38
|
- `algorithm?: string`: Algorithm to use (`SHA-1`, `SHA-256`, `SHA-512`, default: `SHA-256`).
|
|
39
39
|
- `hmacKey: string` (required): Signature HMAC key.
|
|
40
|
+
- `maxNumber?: number` Optional maximum number for the random number generator (defaults to 1,000,000).
|
|
40
41
|
- `number?: number`: Optional number to use. If not provided, a random number will be generated.
|
|
41
42
|
- `salt?: string`: Optional salt string. If not provided, a random salt will be generated.
|
|
43
|
+
- `saltLength?: number` Optional maximum lenght of the random salt (in bytes, defaults to 12).
|
|
42
44
|
|
|
43
45
|
### `verifySolution(payload, hmacKey)`
|
|
44
46
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Algorithm } from './types.js';
|
|
2
|
+
export declare function ab2hex(ab: ArrayBuffer | Uint8Array): string;
|
|
3
|
+
export declare function hash(algorithm: Algorithm, str: string): Promise<string>;
|
|
4
|
+
export declare function hmac(algorithm: Algorithm, str: string, secret: string): Promise<string>;
|
|
5
|
+
export declare function randomBytes(length: number): Uint8Array;
|
|
6
|
+
export declare function randomInt(max: number): number;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.randomInt = exports.randomBytes = exports.hmac = exports.hash = exports.ab2hex = void 0;
|
|
4
|
+
const encoder = new TextEncoder();
|
|
5
|
+
if (!('crypto' in globalThis)) {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
7
|
+
globalThis.crypto = require('node:crypto').webcrypto;
|
|
8
|
+
}
|
|
9
|
+
function ab2hex(ab) {
|
|
10
|
+
return [...new Uint8Array(ab)]
|
|
11
|
+
.map((x) => x.toString(16).padStart(2, '0'))
|
|
12
|
+
.join('');
|
|
13
|
+
}
|
|
14
|
+
exports.ab2hex = ab2hex;
|
|
15
|
+
async function hash(algorithm, str) {
|
|
16
|
+
return ab2hex(await crypto.subtle.digest(algorithm.toUpperCase(), encoder.encode(str)));
|
|
17
|
+
}
|
|
18
|
+
exports.hash = hash;
|
|
19
|
+
async function hmac(algorithm, str, secret) {
|
|
20
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), {
|
|
21
|
+
name: 'HMAC',
|
|
22
|
+
hash: algorithm,
|
|
23
|
+
}, false, ['sign', 'verify']);
|
|
24
|
+
return ab2hex(await crypto.subtle.sign('HMAC', key, encoder.encode(str)));
|
|
25
|
+
}
|
|
26
|
+
exports.hmac = hmac;
|
|
27
|
+
function randomBytes(length) {
|
|
28
|
+
const ab = new Uint8Array(length);
|
|
29
|
+
crypto.getRandomValues(ab);
|
|
30
|
+
return ab;
|
|
31
|
+
}
|
|
32
|
+
exports.randomBytes = randomBytes;
|
|
33
|
+
function randomInt(max) {
|
|
34
|
+
const ab = new Uint32Array(1);
|
|
35
|
+
crypto.getRandomValues(ab);
|
|
36
|
+
const randomNumber = ab[0] / (0xffffffff + 1);
|
|
37
|
+
return Math.floor(randomNumber * max + 1);
|
|
38
|
+
}
|
|
39
|
+
exports.randomInt = randomInt;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifySolution = exports.createChallenge = void 0;
|
|
4
|
+
const helpers_js_1 = require("./helpers.js");
|
|
5
|
+
const DEFAULT_MAX_NUMBER = 1e6;
|
|
6
|
+
const DEFAULT_SALT_LEN = 12;
|
|
7
|
+
const DEFAULT_ALG = 'SHA-256';
|
|
8
|
+
async function createChallenge(options) {
|
|
9
|
+
const algorithm = options.algorithm || DEFAULT_ALG;
|
|
10
|
+
const maxNumber = options.maxNumber || DEFAULT_MAX_NUMBER;
|
|
11
|
+
const saltLength = options.saltLength || DEFAULT_SALT_LEN;
|
|
12
|
+
const salt = options.salt || (0, helpers_js_1.ab2hex)((0, helpers_js_1.randomBytes)(saltLength));
|
|
13
|
+
const number = options.number === void 0 ? (0, helpers_js_1.randomInt)(maxNumber) : options.number;
|
|
14
|
+
const challenge = await (0, helpers_js_1.hash)(algorithm, salt + number);
|
|
15
|
+
return {
|
|
16
|
+
algorithm,
|
|
17
|
+
challenge,
|
|
18
|
+
salt,
|
|
19
|
+
signature: await (0, helpers_js_1.hmac)(algorithm, challenge, options.hmacKey),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
exports.createChallenge = createChallenge;
|
|
23
|
+
async function verifySolution(payload, hmacKey) {
|
|
24
|
+
if (typeof payload === 'string') {
|
|
25
|
+
payload = JSON.parse(atob(payload));
|
|
26
|
+
}
|
|
27
|
+
const check = await createChallenge({
|
|
28
|
+
algorithm: payload.algorithm,
|
|
29
|
+
hmacKey,
|
|
30
|
+
number: payload.number,
|
|
31
|
+
salt: payload.salt,
|
|
32
|
+
});
|
|
33
|
+
return (check.challenge === payload.challenge &&
|
|
34
|
+
check.signature === payload.signature);
|
|
35
|
+
}
|
|
36
|
+
exports.verifySolution = verifySolution;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
|
|
2
|
+
export interface Challenge {
|
|
3
|
+
algorithm: Algorithm;
|
|
4
|
+
challenge: string;
|
|
5
|
+
salt: string;
|
|
6
|
+
signature: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ChallengeOptions {
|
|
9
|
+
algorithm?: Algorithm;
|
|
10
|
+
hmacKey: string;
|
|
11
|
+
maxNumber?: number;
|
|
12
|
+
number?: number;
|
|
13
|
+
salt?: string;
|
|
14
|
+
saltLength?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Payload {
|
|
17
|
+
algorithm: Algorithm;
|
|
18
|
+
challenge: string;
|
|
19
|
+
number: number;
|
|
20
|
+
salt: string;
|
|
21
|
+
signature: string;
|
|
22
|
+
}
|
package/cjs/package.json
ADDED
package/dist/helpers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const encoder = new TextEncoder();
|
|
2
2
|
if (!('crypto' in globalThis)) {
|
|
3
|
-
// @
|
|
4
|
-
globalThis.crypto = (
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
4
|
+
globalThis.crypto = require('node:crypto').webcrypto;
|
|
5
5
|
}
|
|
6
6
|
export function ab2hex(ab) {
|
|
7
7
|
return [...new Uint8Array(ab)]
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { ab2hex, hash, hmac, randomBytes, randomInt } from './helpers.js';
|
|
2
|
-
const DEFAULT_MAX_NUMBER =
|
|
2
|
+
const DEFAULT_MAX_NUMBER = 1e6;
|
|
3
|
+
const DEFAULT_SALT_LEN = 12;
|
|
3
4
|
const DEFAULT_ALG = 'SHA-256';
|
|
4
5
|
export async function createChallenge(options) {
|
|
5
6
|
const algorithm = options.algorithm || DEFAULT_ALG;
|
|
6
|
-
const
|
|
7
|
-
const
|
|
7
|
+
const maxNumber = options.maxNumber || DEFAULT_MAX_NUMBER;
|
|
8
|
+
const saltLength = options.saltLength || DEFAULT_SALT_LEN;
|
|
9
|
+
const salt = options.salt || ab2hex(randomBytes(saltLength));
|
|
10
|
+
const number = options.number === void 0 ? randomInt(maxNumber) : options.number;
|
|
8
11
|
const challenge = await hash(algorithm, salt + number);
|
|
9
12
|
return {
|
|
10
13
|
algorithm,
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "altcha-lib",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"type": "module",
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "rimraf dist && tsc -
|
|
18
|
+
"build": "rimraf dist && rimraf cjs/dist && tsc -b tsconfig.build.json tsconfig.cjs.json",
|
|
19
19
|
"denoify": "rimraf deno_dist && denoify && find deno_dist/. -type f -exec sed -i '' -e 's/node:node:/node:/g' {} +",
|
|
20
20
|
"eslint": "eslint ./lib/**/*",
|
|
21
21
|
"format": "prettier --write './(lib|tests)/**/*'",
|
|
@@ -23,12 +23,19 @@
|
|
|
23
23
|
"test:deno": "deno test tests/deno.ts"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
|
+
"cjs",
|
|
26
27
|
"dist"
|
|
27
28
|
],
|
|
28
29
|
"exports": {
|
|
29
30
|
".": {
|
|
30
31
|
"types": "./dist/index.d.ts",
|
|
31
|
-
"import": "./dist/index.js"
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"require": "./cjs/dist/index.js",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./types": {
|
|
37
|
+
"types": "./dist/types.d.ts",
|
|
38
|
+
"import": "./dist/types.js"
|
|
32
39
|
}
|
|
33
40
|
},
|
|
34
41
|
"typesVersions": {
|