@worldcoin/idkit-core 4.0.1-dev.2039000 → 4.0.1-dev.370b7c0

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
@@ -10,12 +10,10 @@ npm install @worldcoin/idkit-core
10
10
 
11
11
  ## Backend: Generate RP Signature
12
12
 
13
- The RP signature authenticates your verification requests. Generate it server-side:
13
+ The RP signature authenticates your verification requests. Generate it server-side using the `/signing` subpath (pure JS, no WASM init needed):
14
14
 
15
15
  ```typescript
16
- import { IDKit, signRequest } from "@worldcoin/idkit-core";
17
-
18
- await IDKit.initServer();
16
+ import { signRequest } from "@worldcoin/idkit-core/signing";
19
17
 
20
18
  // Never expose RP_SIGNING_KEY to clients
21
19
  const sig = signRequest("my-action", process.env.RP_SIGNING_KEY);
@@ -24,8 +22,8 @@ const sig = signRequest("my-action", process.env.RP_SIGNING_KEY);
24
22
  res.json({
25
23
  sig: sig.sig,
26
24
  nonce: sig.nonce,
27
- created_at: Number(sig.createdAt),
28
- expires_at: Number(sig.expiresAt),
25
+ created_at: sig.createdAt,
26
+ expires_at: sig.expiresAt,
29
27
  });
30
28
  ```
31
29
 
@@ -38,8 +36,6 @@ For common verification scenarios with World ID 3.0 backward compatibility:
38
36
  ```typescript
39
37
  import { IDKit, orbLegacy } from "@worldcoin/idkit-core";
40
38
 
41
- await IDKit.init();
42
-
43
39
  // Fetch signature from your backend
44
40
  const rpSig = await fetch("/api/rp-signature").then((r) => r.json());
45
41
 
@@ -100,3 +96,17 @@ const response = await fetch(
100
96
 
101
97
  const { success } = await response.json();
102
98
  ```
99
+
100
+ ## Subpath Exports
101
+
102
+ Pure JS subpath exports are available for server-side use without WASM initialization:
103
+
104
+ | Subpath | Exports |
105
+ | ---------- | ---------------------------------------------------------------- |
106
+ | `/signing` | `signRequest`, `computeRpSignatureMessage`, `RpSignature` (type) |
107
+ | `/hashing` | `hashSignal` |
108
+
109
+ ```typescript
110
+ import { signRequest } from "@worldcoin/idkit-core/signing";
111
+ import { hashSignal } from "@worldcoin/idkit-core/hashing";
112
+ ```
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ var sha3 = require('@noble/hashes/sha3');
4
+ var utils = require('@noble/hashes/utils');
5
+
6
+ // src/lib/hashing.ts
7
+ function hashToField(input) {
8
+ const hash = BigInt("0x" + utils.bytesToHex(sha3.keccak_256(input))) >> 8n;
9
+ return utils.hexToBytes(hash.toString(16).padStart(64, "0"));
10
+ }
11
+ function hashSignal(signal) {
12
+ let input;
13
+ if (signal instanceof Uint8Array) {
14
+ input = signal;
15
+ } else if (signal.startsWith("0x") && isValidHex(signal.slice(2))) {
16
+ input = utils.hexToBytes(signal.slice(2));
17
+ } else {
18
+ input = new TextEncoder().encode(signal);
19
+ }
20
+ return "0x" + utils.bytesToHex(hashToField(input));
21
+ }
22
+ function isValidHex(s) {
23
+ if (s.length === 0) return false;
24
+ if (s.length % 2 !== 0) return false;
25
+ return /^[0-9a-fA-F]+$/.test(s);
26
+ }
27
+
28
+ exports.hashSignal = hashSignal;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Hashes a signal to its field element hex representation.
3
+ *
4
+ * @param signal - The signal to hash (string or Uint8Array)
5
+ * @returns 0x-prefixed hex string representing the signal hash
6
+ */
7
+ declare function hashSignal(signal: string | Uint8Array): string;
8
+
9
+ export { hashSignal };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Hashes a signal to its field element hex representation.
3
+ *
4
+ * @param signal - The signal to hash (string or Uint8Array)
5
+ * @returns 0x-prefixed hex string representing the signal hash
6
+ */
7
+ declare function hashSignal(signal: string | Uint8Array): string;
8
+
9
+ export { hashSignal };
@@ -0,0 +1,26 @@
1
+ import { keccak_256 } from '@noble/hashes/sha3';
2
+ import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
3
+
4
+ // src/lib/hashing.ts
5
+ function hashToField(input) {
6
+ const hash = BigInt("0x" + bytesToHex(keccak_256(input))) >> 8n;
7
+ return hexToBytes(hash.toString(16).padStart(64, "0"));
8
+ }
9
+ function hashSignal(signal) {
10
+ let input;
11
+ if (signal instanceof Uint8Array) {
12
+ input = signal;
13
+ } else if (signal.startsWith("0x") && isValidHex(signal.slice(2))) {
14
+ input = hexToBytes(signal.slice(2));
15
+ } else {
16
+ input = new TextEncoder().encode(signal);
17
+ }
18
+ return "0x" + bytesToHex(hashToField(input));
19
+ }
20
+ function isValidHex(s) {
21
+ if (s.length === 0) return false;
22
+ if (s.length % 2 !== 0) return false;
23
+ return /^[0-9a-fA-F]+$/.test(s);
24
+ }
25
+
26
+ export { hashSignal };
Binary file
package/dist/index.cjs CHANGED
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ var sha3 = require('@noble/hashes/sha3');
4
+ var utils = require('@noble/hashes/utils');
5
+ var hmac = require('@noble/hashes/hmac');
6
+ var sha2 = require('@noble/hashes/sha2');
7
+ var secp256k1 = require('@noble/secp256k1');
8
+
3
9
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
4
10
  var __defProp = Object.defineProperty;
5
11
  var __export = (target, all2) => {
@@ -388,12 +394,12 @@ function hashSignal(signal) {
388
394
  wasm.__wbindgen_export4(deferred2_0, deferred2_1, 1);
389
395
  }
390
396
  }
391
- function __wasm_bindgen_func_elem_960(arg0, arg1, arg2) {
392
- wasm.__wasm_bindgen_func_elem_960(arg0, arg1, addHeapObject(arg2));
393
- }
394
397
  function __wasm_bindgen_func_elem_597(arg0, arg1) {
395
398
  wasm.__wasm_bindgen_func_elem_597(arg0, arg1);
396
399
  }
400
+ function __wasm_bindgen_func_elem_960(arg0, arg1, arg2) {
401
+ wasm.__wasm_bindgen_func_elem_960(arg0, arg1, addHeapObject(arg2));
402
+ }
397
403
  function __wasm_bindgen_func_elem_1345(arg0, arg1, arg2, arg3) {
398
404
  wasm.__wasm_bindgen_func_elem_1345(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
399
405
  }
@@ -1852,10 +1858,6 @@ var idkit_wasm_default = __wbg_init;
1852
1858
  // src/lib/wasm.ts
1853
1859
  var wasmInitialized = false;
1854
1860
  var wasmInitPromise = null;
1855
- async function importNodeModule(specifier) {
1856
- const dynamicImport = Function("moduleName", "return import(moduleName)");
1857
- return dynamicImport(specifier);
1858
- }
1859
1861
  async function initIDKit() {
1860
1862
  if (wasmInitialized) {
1861
1863
  return;
@@ -1874,36 +1876,6 @@ async function initIDKit() {
1874
1876
  })();
1875
1877
  return wasmInitPromise;
1876
1878
  }
1877
- async function initIDKitServer() {
1878
- if (wasmInitialized) {
1879
- return;
1880
- }
1881
- if (wasmInitPromise) {
1882
- return wasmInitPromise;
1883
- }
1884
- wasmInitPromise = (async () => {
1885
- try {
1886
- const { readFile } = await importNodeModule(
1887
- "node:fs/promises"
1888
- );
1889
- const { fileURLToPath } = await importNodeModule(
1890
- "node:url"
1891
- );
1892
- const { dirname, join } = await importNodeModule(
1893
- "node:path"
1894
- );
1895
- const modulePath = fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
1896
- const wasmPath = join(dirname(modulePath), "idkit_wasm_bg.wasm");
1897
- const wasmBuffer = await readFile(wasmPath);
1898
- await idkit_wasm_default({ module_or_path: wasmBuffer });
1899
- wasmInitialized = true;
1900
- } catch (error) {
1901
- wasmInitPromise = null;
1902
- throw new Error(`Failed to initialize IDKit WASM for server: ${error}`);
1903
- }
1904
- })();
1905
- return wasmInitPromise;
1906
- }
1907
1879
 
1908
1880
  // src/transports/native.ts
1909
1881
  function isInWorldApp() {
@@ -2313,10 +2285,6 @@ function proveSession2(sessionId, config) {
2313
2285
  });
2314
2286
  }
2315
2287
  var IDKit = {
2316
- /** Initialize WASM for browser environments (not needed in World App) */
2317
- init: initIDKit,
2318
- /** Initialize WASM for Node.js/server environments */
2319
- initServer: initIDKitServer,
2320
2288
  /** Create a new verification request */
2321
2289
  request: createRequest,
2322
2290
  /** Create a new session (no action, no existing session_id) */
@@ -2359,21 +2327,71 @@ var isServerEnvironment = () => {
2359
2327
  }
2360
2328
  return false;
2361
2329
  };
2330
+ function hashToField(input) {
2331
+ const hash = BigInt("0x" + utils.bytesToHex(sha3.keccak_256(input))) >> 8n;
2332
+ return utils.hexToBytes(hash.toString(16).padStart(64, "0"));
2333
+ }
2334
+ function hashSignal2(signal) {
2335
+ let input;
2336
+ if (signal instanceof Uint8Array) {
2337
+ input = signal;
2338
+ } else if (signal.startsWith("0x") && isValidHex(signal.slice(2))) {
2339
+ input = utils.hexToBytes(signal.slice(2));
2340
+ } else {
2341
+ input = new TextEncoder().encode(signal);
2342
+ }
2343
+ return "0x" + utils.bytesToHex(hashToField(input));
2344
+ }
2345
+ function isValidHex(s) {
2346
+ if (s.length === 0) return false;
2347
+ if (s.length % 2 !== 0) return false;
2348
+ return /^[0-9a-fA-F]+$/.test(s);
2349
+ }
2362
2350
 
2363
- // src/lib/rp-signature.ts
2364
- function signRequest2(action, signingKeyHex, ttlSeconds) {
2351
+ // src/lib/signing.ts
2352
+ secp256k1.etc.hmacSha256Sync = (key, ...msgs) => hmac.hmac(sha2.sha256, key, secp256k1.etc.concatBytes(...msgs));
2353
+ var DEFAULT_TTL_SEC = 300;
2354
+ function computeRpSignatureMessage(nonceBytes, createdAt, expiresAt) {
2355
+ const message = new Uint8Array(48);
2356
+ message.set(nonceBytes, 0);
2357
+ const view = new DataView(message.buffer);
2358
+ view.setBigUint64(32, BigInt(createdAt), false);
2359
+ view.setBigUint64(40, BigInt(expiresAt), false);
2360
+ return message;
2361
+ }
2362
+ function signRequest2(_action, signingKeyHex, ttl = DEFAULT_TTL_SEC) {
2365
2363
  if (!isServerEnvironment()) {
2366
2364
  throw new Error(
2367
2365
  "signRequest can only be used in Node.js environments. This function requires access to signing keys and should never be called from browser/client-side code."
2368
2366
  );
2369
2367
  }
2370
- const ttlBigInt = ttlSeconds !== void 0 ? BigInt(ttlSeconds) : void 0;
2371
- return idkit_wasm_exports.signRequest(action, signingKeyHex, ttlBigInt);
2372
- }
2373
-
2374
- // src/lib/hashing.ts
2375
- function hashSignal2(signal) {
2376
- return idkit_wasm_exports.hashSignal(signal);
2368
+ const keyHex = signingKeyHex.startsWith("0x") ? signingKeyHex.slice(2) : signingKeyHex;
2369
+ if (!/^[0-9a-fA-F]+$/.test(keyHex)) {
2370
+ throw new Error("Invalid signing key: contains non-hex characters");
2371
+ }
2372
+ if (keyHex.length !== 64) {
2373
+ throw new Error(
2374
+ `Invalid signing key: expected 32 bytes (64 hex chars), got ${keyHex.length / 2} bytes`
2375
+ );
2376
+ }
2377
+ const privKey = secp256k1.etc.hexToBytes(keyHex);
2378
+ const randomBytes = crypto.getRandomValues(new Uint8Array(32));
2379
+ const nonceBytes = hashToField(randomBytes);
2380
+ const createdAt = Math.floor(Date.now() / 1e3);
2381
+ const expiresAt = createdAt + ttl;
2382
+ const message = computeRpSignatureMessage(nonceBytes, createdAt, expiresAt);
2383
+ const msgHash = sha3.keccak_256(message);
2384
+ const recSig = secp256k1.sign(msgHash, privKey);
2385
+ const compact = recSig.toCompactRawBytes();
2386
+ const sig65 = new Uint8Array(65);
2387
+ sig65.set(compact, 0);
2388
+ sig65[64] = recSig.recovery + 27;
2389
+ return {
2390
+ sig: "0x" + utils.bytesToHex(sig65),
2391
+ nonce: "0x" + utils.bytesToHex(nonceBytes),
2392
+ createdAt,
2393
+ expiresAt
2394
+ };
2377
2395
  }
2378
2396
 
2379
2397
  exports.CredentialRequest = CredentialRequest;
package/dist/index.d.cts CHANGED
@@ -1,3 +1,6 @@
1
+ export { RpSignature, signRequest } from './signing.cjs';
2
+ export { hashSignal } from './hashing.cjs';
3
+
1
4
  /**
2
5
  * Configuration types for IDKit
3
6
  *
@@ -240,64 +243,6 @@ type ConstraintNode =
240
243
  | { any: ConstraintNode[] }
241
244
  | { all: ConstraintNode[] };
242
245
 
243
-
244
-
245
- interface RpSignature {
246
- sig: string;
247
- nonce: string;
248
- createdAt: number;
249
- expiresAt: number;
250
- toJSON(): { sig: string; nonce: string; createdAt: number; expiresAt: number };
251
- }
252
- declare class RpSignature {
253
- private constructor();
254
- free(): void;
255
- [Symbol.dispose](): void;
256
- /**
257
- * Converts to JSON
258
- *
259
- * # Errors
260
- *
261
- * Returns an error if setting object properties fails
262
- */
263
- toJSON(): any;
264
- /**
265
- * Gets the creation timestamp
266
- */
267
- readonly createdAt: bigint;
268
- /**
269
- * Gets the expiration timestamp
270
- */
271
- readonly expiresAt: bigint;
272
- /**
273
- * Gets the signature as hex string (0x-prefixed, 65 bytes)
274
- */
275
- readonly sig: string;
276
- /**
277
- * Gets the nonce as hex string (0x-prefixed field element)
278
- */
279
- readonly nonce: string;
280
- }
281
-
282
- /**
283
- * WASM initialization and management
284
- */
285
-
286
- /**
287
- * Initializes the WASM module for browser environments
288
- * Uses fetch-based loading (works with http/https URLs)
289
- * This must be called before using any WASM-powered functions
290
- * Safe to call multiple times - initialization only happens once
291
- */
292
- declare function initIDKit(): Promise<void>;
293
- /**
294
- * Initializes the WASM module for Node.js/server environments
295
- * Uses fs-based loading since Node.js fetch doesn't support file:// URLs
296
- * This must be called before using any WASM-powered functions
297
- * Safe to call multiple times - initialization only happens once
298
- */
299
- declare function initIDKitServer(): Promise<void>;
300
-
301
246
  /**
302
247
  * Result types - re-exported from WASM bindings
303
248
  *
@@ -641,10 +586,6 @@ declare function proveSession(sessionId: string, config: IDKitSessionConfig): ID
641
586
  * ```
642
587
  */
643
588
  declare const IDKit: {
644
- /** Initialize WASM for browser environments (not needed in World App) */
645
- init: typeof initIDKit;
646
- /** Initialize WASM for Node.js/server environments */
647
- initServer: typeof initIDKitServer;
648
589
  /** Create a new verification request */
649
590
  request: typeof createRequest;
650
591
  /** Create a new session (no action, no existing session_id) */
@@ -687,39 +628,4 @@ declare const isWeb: () => boolean;
687
628
  */
688
629
  declare const isNode: () => boolean;
689
630
 
690
- /**
691
- * Signs an RP request for World ID proof verification
692
- *
693
- * **Backend-only**: This function should ONLY be used in Node.js/server environments.
694
- * Never use this in browser/client-side code as it requires access to your signing key.
695
- *
696
- * This function generates a cryptographic signature that authenticates your proof request.
697
- * The returned signature, nonce, and timestamps should be passed as `rp_context` to the client.
698
- *
699
- * @param action - The action tied to the proof request
700
- * @param signingKeyHex - The ECDSA private key as hex (0x-prefixed or not, 32 bytes)
701
- * @param ttlSeconds - Optional time-to-live in seconds (defaults to 300 = 5 minutes)
702
- * @returns RpSignature object with sig, nonce, createdAt, expiresAt to use as rp_context
703
- * @throws Error if called in non-Node.js environment or if parameters are invalid
704
- *
705
- * @example
706
- * ```typescript
707
- * import { signRequest } from '@worldcoin/idkit-core'
708
- *
709
- * const signingKey = process.env.RP_SIGNING_KEY // Load from secure env var
710
- * const signature = signRequest('my-action', signingKey)
711
- * console.log(signature.sig, signature.nonce, signature.createdAt, signature.expiresAt)
712
- * ```
713
- */
714
- declare function signRequest(action: string, signingKeyHex: string, ttlSeconds?: number): RpSignature;
715
-
716
- /**
717
- * Hashes a Signal (string or Uint8Array) to its hash representation.
718
- * This is the same hashing used internally when constructing proof requests.
719
- *
720
- * @param signal - The signal to hash (string or Uint8Array)
721
- * @returns 0x-prefixed hex string representing the signal hash
722
- */
723
- declare function hashSignal(signal: string | Uint8Array): string;
724
-
725
- export { type AbiEncodedValue, type ConstraintNode, CredentialRequest, type CredentialRequestType, type CredentialType, type DocumentLegacyPreset, IDKit, type IDKitCompletionResult, type IDKitErrorCode, IDKitErrorCodes, type IDKitRequest, type IDKitRequestConfig, type IDKitResult, type IDKitResultSession, type IDKitSessionConfig, type OrbLegacyPreset, type Preset, type ResponseItemSession, type ResponseItemV3, type ResponseItemV4, type RpContext, RpSignature, type SecureDocumentLegacyPreset, type Status$1 as Status, type WaitOptions, all, any, documentLegacy, hashSignal, isNode, isReactNative, isWeb, orbLegacy, secureDocumentLegacy, signRequest };
631
+ export { type AbiEncodedValue, type ConstraintNode, CredentialRequest, type CredentialRequestType, type CredentialType, type DocumentLegacyPreset, IDKit, type IDKitCompletionResult, type IDKitErrorCode, IDKitErrorCodes, type IDKitRequest, type IDKitRequestConfig, type IDKitResult, type IDKitResultSession, type IDKitSessionConfig, type OrbLegacyPreset, type Preset, type ResponseItemSession, type ResponseItemV3, type ResponseItemV4, type RpContext, type SecureDocumentLegacyPreset, type Status$1 as Status, type WaitOptions, all, any, documentLegacy, isNode, isReactNative, isWeb, orbLegacy, secureDocumentLegacy };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export { RpSignature, signRequest } from './signing.js';
2
+ export { hashSignal } from './hashing.js';
3
+
1
4
  /**
2
5
  * Configuration types for IDKit
3
6
  *
@@ -240,64 +243,6 @@ type ConstraintNode =
240
243
  | { any: ConstraintNode[] }
241
244
  | { all: ConstraintNode[] };
242
245
 
243
-
244
-
245
- interface RpSignature {
246
- sig: string;
247
- nonce: string;
248
- createdAt: number;
249
- expiresAt: number;
250
- toJSON(): { sig: string; nonce: string; createdAt: number; expiresAt: number };
251
- }
252
- declare class RpSignature {
253
- private constructor();
254
- free(): void;
255
- [Symbol.dispose](): void;
256
- /**
257
- * Converts to JSON
258
- *
259
- * # Errors
260
- *
261
- * Returns an error if setting object properties fails
262
- */
263
- toJSON(): any;
264
- /**
265
- * Gets the creation timestamp
266
- */
267
- readonly createdAt: bigint;
268
- /**
269
- * Gets the expiration timestamp
270
- */
271
- readonly expiresAt: bigint;
272
- /**
273
- * Gets the signature as hex string (0x-prefixed, 65 bytes)
274
- */
275
- readonly sig: string;
276
- /**
277
- * Gets the nonce as hex string (0x-prefixed field element)
278
- */
279
- readonly nonce: string;
280
- }
281
-
282
- /**
283
- * WASM initialization and management
284
- */
285
-
286
- /**
287
- * Initializes the WASM module for browser environments
288
- * Uses fetch-based loading (works with http/https URLs)
289
- * This must be called before using any WASM-powered functions
290
- * Safe to call multiple times - initialization only happens once
291
- */
292
- declare function initIDKit(): Promise<void>;
293
- /**
294
- * Initializes the WASM module for Node.js/server environments
295
- * Uses fs-based loading since Node.js fetch doesn't support file:// URLs
296
- * This must be called before using any WASM-powered functions
297
- * Safe to call multiple times - initialization only happens once
298
- */
299
- declare function initIDKitServer(): Promise<void>;
300
-
301
246
  /**
302
247
  * Result types - re-exported from WASM bindings
303
248
  *
@@ -641,10 +586,6 @@ declare function proveSession(sessionId: string, config: IDKitSessionConfig): ID
641
586
  * ```
642
587
  */
643
588
  declare const IDKit: {
644
- /** Initialize WASM for browser environments (not needed in World App) */
645
- init: typeof initIDKit;
646
- /** Initialize WASM for Node.js/server environments */
647
- initServer: typeof initIDKitServer;
648
589
  /** Create a new verification request */
649
590
  request: typeof createRequest;
650
591
  /** Create a new session (no action, no existing session_id) */
@@ -687,39 +628,4 @@ declare const isWeb: () => boolean;
687
628
  */
688
629
  declare const isNode: () => boolean;
689
630
 
690
- /**
691
- * Signs an RP request for World ID proof verification
692
- *
693
- * **Backend-only**: This function should ONLY be used in Node.js/server environments.
694
- * Never use this in browser/client-side code as it requires access to your signing key.
695
- *
696
- * This function generates a cryptographic signature that authenticates your proof request.
697
- * The returned signature, nonce, and timestamps should be passed as `rp_context` to the client.
698
- *
699
- * @param action - The action tied to the proof request
700
- * @param signingKeyHex - The ECDSA private key as hex (0x-prefixed or not, 32 bytes)
701
- * @param ttlSeconds - Optional time-to-live in seconds (defaults to 300 = 5 minutes)
702
- * @returns RpSignature object with sig, nonce, createdAt, expiresAt to use as rp_context
703
- * @throws Error if called in non-Node.js environment or if parameters are invalid
704
- *
705
- * @example
706
- * ```typescript
707
- * import { signRequest } from '@worldcoin/idkit-core'
708
- *
709
- * const signingKey = process.env.RP_SIGNING_KEY // Load from secure env var
710
- * const signature = signRequest('my-action', signingKey)
711
- * console.log(signature.sig, signature.nonce, signature.createdAt, signature.expiresAt)
712
- * ```
713
- */
714
- declare function signRequest(action: string, signingKeyHex: string, ttlSeconds?: number): RpSignature;
715
-
716
- /**
717
- * Hashes a Signal (string or Uint8Array) to its hash representation.
718
- * This is the same hashing used internally when constructing proof requests.
719
- *
720
- * @param signal - The signal to hash (string or Uint8Array)
721
- * @returns 0x-prefixed hex string representing the signal hash
722
- */
723
- declare function hashSignal(signal: string | Uint8Array): string;
724
-
725
- export { type AbiEncodedValue, type ConstraintNode, CredentialRequest, type CredentialRequestType, type CredentialType, type DocumentLegacyPreset, IDKit, type IDKitCompletionResult, type IDKitErrorCode, IDKitErrorCodes, type IDKitRequest, type IDKitRequestConfig, type IDKitResult, type IDKitResultSession, type IDKitSessionConfig, type OrbLegacyPreset, type Preset, type ResponseItemSession, type ResponseItemV3, type ResponseItemV4, type RpContext, RpSignature, type SecureDocumentLegacyPreset, type Status$1 as Status, type WaitOptions, all, any, documentLegacy, hashSignal, isNode, isReactNative, isWeb, orbLegacy, secureDocumentLegacy, signRequest };
631
+ export { type AbiEncodedValue, type ConstraintNode, CredentialRequest, type CredentialRequestType, type CredentialType, type DocumentLegacyPreset, IDKit, type IDKitCompletionResult, type IDKitErrorCode, IDKitErrorCodes, type IDKitRequest, type IDKitRequestConfig, type IDKitResult, type IDKitResultSession, type IDKitSessionConfig, type OrbLegacyPreset, type Preset, type ResponseItemSession, type ResponseItemV3, type ResponseItemV4, type RpContext, type SecureDocumentLegacyPreset, type Status$1 as Status, type WaitOptions, all, any, documentLegacy, isNode, isReactNative, isWeb, orbLegacy, secureDocumentLegacy };
package/dist/index.js CHANGED
@@ -1,3 +1,9 @@
1
+ import { keccak_256 } from '@noble/hashes/sha3';
2
+ import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
3
+ import { hmac } from '@noble/hashes/hmac';
4
+ import { sha256 } from '@noble/hashes/sha2';
5
+ import { etc, sign } from '@noble/secp256k1';
6
+
1
7
  var __defProp = Object.defineProperty;
2
8
  var __export = (target, all2) => {
3
9
  for (var name in all2)
@@ -385,12 +391,12 @@ function hashSignal(signal) {
385
391
  wasm.__wbindgen_export4(deferred2_0, deferred2_1, 1);
386
392
  }
387
393
  }
388
- function __wasm_bindgen_func_elem_960(arg0, arg1, arg2) {
389
- wasm.__wasm_bindgen_func_elem_960(arg0, arg1, addHeapObject(arg2));
390
- }
391
394
  function __wasm_bindgen_func_elem_597(arg0, arg1) {
392
395
  wasm.__wasm_bindgen_func_elem_597(arg0, arg1);
393
396
  }
397
+ function __wasm_bindgen_func_elem_960(arg0, arg1, arg2) {
398
+ wasm.__wasm_bindgen_func_elem_960(arg0, arg1, addHeapObject(arg2));
399
+ }
394
400
  function __wasm_bindgen_func_elem_1345(arg0, arg1, arg2, arg3) {
395
401
  wasm.__wasm_bindgen_func_elem_1345(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
396
402
  }
@@ -1849,10 +1855,6 @@ var idkit_wasm_default = __wbg_init;
1849
1855
  // src/lib/wasm.ts
1850
1856
  var wasmInitialized = false;
1851
1857
  var wasmInitPromise = null;
1852
- async function importNodeModule(specifier) {
1853
- const dynamicImport = Function("moduleName", "return import(moduleName)");
1854
- return dynamicImport(specifier);
1855
- }
1856
1858
  async function initIDKit() {
1857
1859
  if (wasmInitialized) {
1858
1860
  return;
@@ -1871,36 +1873,6 @@ async function initIDKit() {
1871
1873
  })();
1872
1874
  return wasmInitPromise;
1873
1875
  }
1874
- async function initIDKitServer() {
1875
- if (wasmInitialized) {
1876
- return;
1877
- }
1878
- if (wasmInitPromise) {
1879
- return wasmInitPromise;
1880
- }
1881
- wasmInitPromise = (async () => {
1882
- try {
1883
- const { readFile } = await importNodeModule(
1884
- "node:fs/promises"
1885
- );
1886
- const { fileURLToPath } = await importNodeModule(
1887
- "node:url"
1888
- );
1889
- const { dirname, join } = await importNodeModule(
1890
- "node:path"
1891
- );
1892
- const modulePath = fileURLToPath(import.meta.url);
1893
- const wasmPath = join(dirname(modulePath), "idkit_wasm_bg.wasm");
1894
- const wasmBuffer = await readFile(wasmPath);
1895
- await idkit_wasm_default({ module_or_path: wasmBuffer });
1896
- wasmInitialized = true;
1897
- } catch (error) {
1898
- wasmInitPromise = null;
1899
- throw new Error(`Failed to initialize IDKit WASM for server: ${error}`);
1900
- }
1901
- })();
1902
- return wasmInitPromise;
1903
- }
1904
1876
 
1905
1877
  // src/transports/native.ts
1906
1878
  function isInWorldApp() {
@@ -2310,10 +2282,6 @@ function proveSession2(sessionId, config) {
2310
2282
  });
2311
2283
  }
2312
2284
  var IDKit = {
2313
- /** Initialize WASM for browser environments (not needed in World App) */
2314
- init: initIDKit,
2315
- /** Initialize WASM for Node.js/server environments */
2316
- initServer: initIDKitServer,
2317
2285
  /** Create a new verification request */
2318
2286
  request: createRequest,
2319
2287
  /** Create a new session (no action, no existing session_id) */
@@ -2356,21 +2324,71 @@ var isServerEnvironment = () => {
2356
2324
  }
2357
2325
  return false;
2358
2326
  };
2327
+ function hashToField(input) {
2328
+ const hash = BigInt("0x" + bytesToHex(keccak_256(input))) >> 8n;
2329
+ return hexToBytes(hash.toString(16).padStart(64, "0"));
2330
+ }
2331
+ function hashSignal2(signal) {
2332
+ let input;
2333
+ if (signal instanceof Uint8Array) {
2334
+ input = signal;
2335
+ } else if (signal.startsWith("0x") && isValidHex(signal.slice(2))) {
2336
+ input = hexToBytes(signal.slice(2));
2337
+ } else {
2338
+ input = new TextEncoder().encode(signal);
2339
+ }
2340
+ return "0x" + bytesToHex(hashToField(input));
2341
+ }
2342
+ function isValidHex(s) {
2343
+ if (s.length === 0) return false;
2344
+ if (s.length % 2 !== 0) return false;
2345
+ return /^[0-9a-fA-F]+$/.test(s);
2346
+ }
2359
2347
 
2360
- // src/lib/rp-signature.ts
2361
- function signRequest2(action, signingKeyHex, ttlSeconds) {
2348
+ // src/lib/signing.ts
2349
+ etc.hmacSha256Sync = (key, ...msgs) => hmac(sha256, key, etc.concatBytes(...msgs));
2350
+ var DEFAULT_TTL_SEC = 300;
2351
+ function computeRpSignatureMessage(nonceBytes, createdAt, expiresAt) {
2352
+ const message = new Uint8Array(48);
2353
+ message.set(nonceBytes, 0);
2354
+ const view = new DataView(message.buffer);
2355
+ view.setBigUint64(32, BigInt(createdAt), false);
2356
+ view.setBigUint64(40, BigInt(expiresAt), false);
2357
+ return message;
2358
+ }
2359
+ function signRequest2(_action, signingKeyHex, ttl = DEFAULT_TTL_SEC) {
2362
2360
  if (!isServerEnvironment()) {
2363
2361
  throw new Error(
2364
2362
  "signRequest can only be used in Node.js environments. This function requires access to signing keys and should never be called from browser/client-side code."
2365
2363
  );
2366
2364
  }
2367
- const ttlBigInt = ttlSeconds !== void 0 ? BigInt(ttlSeconds) : void 0;
2368
- return idkit_wasm_exports.signRequest(action, signingKeyHex, ttlBigInt);
2369
- }
2370
-
2371
- // src/lib/hashing.ts
2372
- function hashSignal2(signal) {
2373
- return idkit_wasm_exports.hashSignal(signal);
2365
+ const keyHex = signingKeyHex.startsWith("0x") ? signingKeyHex.slice(2) : signingKeyHex;
2366
+ if (!/^[0-9a-fA-F]+$/.test(keyHex)) {
2367
+ throw new Error("Invalid signing key: contains non-hex characters");
2368
+ }
2369
+ if (keyHex.length !== 64) {
2370
+ throw new Error(
2371
+ `Invalid signing key: expected 32 bytes (64 hex chars), got ${keyHex.length / 2} bytes`
2372
+ );
2373
+ }
2374
+ const privKey = etc.hexToBytes(keyHex);
2375
+ const randomBytes = crypto.getRandomValues(new Uint8Array(32));
2376
+ const nonceBytes = hashToField(randomBytes);
2377
+ const createdAt = Math.floor(Date.now() / 1e3);
2378
+ const expiresAt = createdAt + ttl;
2379
+ const message = computeRpSignatureMessage(nonceBytes, createdAt, expiresAt);
2380
+ const msgHash = keccak_256(message);
2381
+ const recSig = sign(msgHash, privKey);
2382
+ const compact = recSig.toCompactRawBytes();
2383
+ const sig65 = new Uint8Array(65);
2384
+ sig65.set(compact, 0);
2385
+ sig65[64] = recSig.recovery + 27;
2386
+ return {
2387
+ sig: "0x" + bytesToHex(sig65),
2388
+ nonce: "0x" + bytesToHex(nonceBytes),
2389
+ createdAt,
2390
+ expiresAt
2391
+ };
2374
2392
  }
2375
2393
 
2376
2394
  export { CredentialRequest, IDKit, IDKitErrorCodes, all, any, documentLegacy, hashSignal2 as hashSignal, isNode, isReactNative, isWeb, orbLegacy, secureDocumentLegacy, signRequest2 as signRequest };
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ var sha3 = require('@noble/hashes/sha3');
4
+ var utils = require('@noble/hashes/utils');
5
+ var hmac = require('@noble/hashes/hmac');
6
+ var sha2 = require('@noble/hashes/sha2');
7
+ var secp256k1 = require('@noble/secp256k1');
8
+
9
+ // src/lib/signing.ts
10
+
11
+ // src/lib/platform.ts
12
+ var isServerEnvironment = () => {
13
+ if (typeof process !== "undefined" && process.versions?.node) {
14
+ return true;
15
+ }
16
+ if (typeof globalThis.Deno !== "undefined") {
17
+ return true;
18
+ }
19
+ if (typeof globalThis.Bun !== "undefined") {
20
+ return true;
21
+ }
22
+ return false;
23
+ };
24
+ function hashToField(input) {
25
+ const hash = BigInt("0x" + utils.bytesToHex(sha3.keccak_256(input))) >> 8n;
26
+ return utils.hexToBytes(hash.toString(16).padStart(64, "0"));
27
+ }
28
+
29
+ // src/lib/signing.ts
30
+ secp256k1.etc.hmacSha256Sync = (key, ...msgs) => hmac.hmac(sha2.sha256, key, secp256k1.etc.concatBytes(...msgs));
31
+ var DEFAULT_TTL_SEC = 300;
32
+ function computeRpSignatureMessage(nonceBytes, createdAt, expiresAt) {
33
+ const message = new Uint8Array(48);
34
+ message.set(nonceBytes, 0);
35
+ const view = new DataView(message.buffer);
36
+ view.setBigUint64(32, BigInt(createdAt), false);
37
+ view.setBigUint64(40, BigInt(expiresAt), false);
38
+ return message;
39
+ }
40
+ function signRequest(_action, signingKeyHex, ttl = DEFAULT_TTL_SEC) {
41
+ if (!isServerEnvironment()) {
42
+ throw new Error(
43
+ "signRequest can only be used in Node.js environments. This function requires access to signing keys and should never be called from browser/client-side code."
44
+ );
45
+ }
46
+ const keyHex = signingKeyHex.startsWith("0x") ? signingKeyHex.slice(2) : signingKeyHex;
47
+ if (!/^[0-9a-fA-F]+$/.test(keyHex)) {
48
+ throw new Error("Invalid signing key: contains non-hex characters");
49
+ }
50
+ if (keyHex.length !== 64) {
51
+ throw new Error(
52
+ `Invalid signing key: expected 32 bytes (64 hex chars), got ${keyHex.length / 2} bytes`
53
+ );
54
+ }
55
+ const privKey = secp256k1.etc.hexToBytes(keyHex);
56
+ const randomBytes = crypto.getRandomValues(new Uint8Array(32));
57
+ const nonceBytes = hashToField(randomBytes);
58
+ const createdAt = Math.floor(Date.now() / 1e3);
59
+ const expiresAt = createdAt + ttl;
60
+ const message = computeRpSignatureMessage(nonceBytes, createdAt, expiresAt);
61
+ const msgHash = sha3.keccak_256(message);
62
+ const recSig = secp256k1.sign(msgHash, privKey);
63
+ const compact = recSig.toCompactRawBytes();
64
+ const sig65 = new Uint8Array(65);
65
+ sig65.set(compact, 0);
66
+ sig65[64] = recSig.recovery + 27;
67
+ return {
68
+ sig: "0x" + utils.bytesToHex(sig65),
69
+ nonce: "0x" + utils.bytesToHex(nonceBytes),
70
+ createdAt,
71
+ expiresAt
72
+ };
73
+ }
74
+
75
+ exports.computeRpSignatureMessage = computeRpSignatureMessage;
76
+ exports.signRequest = signRequest;
@@ -0,0 +1,36 @@
1
+ interface RpSignature {
2
+ sig: string;
3
+ nonce: string;
4
+ createdAt: number;
5
+ expiresAt: number;
6
+ }
7
+ /**
8
+ * Builds the 48-byte message that gets signed for RP signature verification.
9
+ *
10
+ * Message format: nonce(32) || createdAt_u64_be(8) || expiresAt_u64_be(8)
11
+ *
12
+ * Matches Rust `compute_rp_signature_msg`:
13
+ * https://github.com/worldcoin/world-id-protocol/blob/0008eab1efe200e572f27258793f9be5cb32858b/crates/primitives/src/rp.rs#L95-L105
14
+ *
15
+ * @param nonceBytes - 32-byte nonce as Uint8Array
16
+ * @param createdAt - unix timestamp in seconds
17
+ * @param expiresAt - unix timestamp in seconds
18
+ * @returns 48-byte message ready to be hashed and signed
19
+ */
20
+ declare function computeRpSignatureMessage(nonceBytes: Uint8Array, createdAt: number, expiresAt: number): Uint8Array;
21
+ /**
22
+ * Signs an RP request using pure JS (no WASM required).
23
+ *
24
+ * Algorithm matches Rust implementation in rust/core/src/rp_signature.rs
25
+ *
26
+ * Nonce generation matches `from_arbitrary_raw_bytes`:
27
+ * https://github.com/worldcoin/world-id-protocol/blob/31405df8bcd5a2784e04ad9890cf095111dcac13/crates/primitives/src/lib.rs#L134-L149
28
+ *
29
+ * @param action - The action tied to the proof request (accepted for API compat, not used in signature)
30
+ * @param signingKeyHex - The ECDSA private key as hex (0x-prefixed or not, 32 bytes)
31
+ * @param ttl - Time-to-live in seconds (defaults to 300 = 5 minutes)
32
+ * @returns RpSignature object with sig, nonce, createdAt, expiresAt
33
+ */
34
+ declare function signRequest(_action: string, signingKeyHex: string, ttl?: number): RpSignature;
35
+
36
+ export { type RpSignature, computeRpSignatureMessage, signRequest };
@@ -0,0 +1,36 @@
1
+ interface RpSignature {
2
+ sig: string;
3
+ nonce: string;
4
+ createdAt: number;
5
+ expiresAt: number;
6
+ }
7
+ /**
8
+ * Builds the 48-byte message that gets signed for RP signature verification.
9
+ *
10
+ * Message format: nonce(32) || createdAt_u64_be(8) || expiresAt_u64_be(8)
11
+ *
12
+ * Matches Rust `compute_rp_signature_msg`:
13
+ * https://github.com/worldcoin/world-id-protocol/blob/0008eab1efe200e572f27258793f9be5cb32858b/crates/primitives/src/rp.rs#L95-L105
14
+ *
15
+ * @param nonceBytes - 32-byte nonce as Uint8Array
16
+ * @param createdAt - unix timestamp in seconds
17
+ * @param expiresAt - unix timestamp in seconds
18
+ * @returns 48-byte message ready to be hashed and signed
19
+ */
20
+ declare function computeRpSignatureMessage(nonceBytes: Uint8Array, createdAt: number, expiresAt: number): Uint8Array;
21
+ /**
22
+ * Signs an RP request using pure JS (no WASM required).
23
+ *
24
+ * Algorithm matches Rust implementation in rust/core/src/rp_signature.rs
25
+ *
26
+ * Nonce generation matches `from_arbitrary_raw_bytes`:
27
+ * https://github.com/worldcoin/world-id-protocol/blob/31405df8bcd5a2784e04ad9890cf095111dcac13/crates/primitives/src/lib.rs#L134-L149
28
+ *
29
+ * @param action - The action tied to the proof request (accepted for API compat, not used in signature)
30
+ * @param signingKeyHex - The ECDSA private key as hex (0x-prefixed or not, 32 bytes)
31
+ * @param ttl - Time-to-live in seconds (defaults to 300 = 5 minutes)
32
+ * @returns RpSignature object with sig, nonce, createdAt, expiresAt
33
+ */
34
+ declare function signRequest(_action: string, signingKeyHex: string, ttl?: number): RpSignature;
35
+
36
+ export { type RpSignature, computeRpSignatureMessage, signRequest };
@@ -0,0 +1,73 @@
1
+ import { keccak_256 } from '@noble/hashes/sha3';
2
+ import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
3
+ import { hmac } from '@noble/hashes/hmac';
4
+ import { sha256 } from '@noble/hashes/sha2';
5
+ import { etc, sign } from '@noble/secp256k1';
6
+
7
+ // src/lib/signing.ts
8
+
9
+ // src/lib/platform.ts
10
+ var isServerEnvironment = () => {
11
+ if (typeof process !== "undefined" && process.versions?.node) {
12
+ return true;
13
+ }
14
+ if (typeof globalThis.Deno !== "undefined") {
15
+ return true;
16
+ }
17
+ if (typeof globalThis.Bun !== "undefined") {
18
+ return true;
19
+ }
20
+ return false;
21
+ };
22
+ function hashToField(input) {
23
+ const hash = BigInt("0x" + bytesToHex(keccak_256(input))) >> 8n;
24
+ return hexToBytes(hash.toString(16).padStart(64, "0"));
25
+ }
26
+
27
+ // src/lib/signing.ts
28
+ etc.hmacSha256Sync = (key, ...msgs) => hmac(sha256, key, etc.concatBytes(...msgs));
29
+ var DEFAULT_TTL_SEC = 300;
30
+ function computeRpSignatureMessage(nonceBytes, createdAt, expiresAt) {
31
+ const message = new Uint8Array(48);
32
+ message.set(nonceBytes, 0);
33
+ const view = new DataView(message.buffer);
34
+ view.setBigUint64(32, BigInt(createdAt), false);
35
+ view.setBigUint64(40, BigInt(expiresAt), false);
36
+ return message;
37
+ }
38
+ function signRequest(_action, signingKeyHex, ttl = DEFAULT_TTL_SEC) {
39
+ if (!isServerEnvironment()) {
40
+ throw new Error(
41
+ "signRequest can only be used in Node.js environments. This function requires access to signing keys and should never be called from browser/client-side code."
42
+ );
43
+ }
44
+ const keyHex = signingKeyHex.startsWith("0x") ? signingKeyHex.slice(2) : signingKeyHex;
45
+ if (!/^[0-9a-fA-F]+$/.test(keyHex)) {
46
+ throw new Error("Invalid signing key: contains non-hex characters");
47
+ }
48
+ if (keyHex.length !== 64) {
49
+ throw new Error(
50
+ `Invalid signing key: expected 32 bytes (64 hex chars), got ${keyHex.length / 2} bytes`
51
+ );
52
+ }
53
+ const privKey = etc.hexToBytes(keyHex);
54
+ const randomBytes = crypto.getRandomValues(new Uint8Array(32));
55
+ const nonceBytes = hashToField(randomBytes);
56
+ const createdAt = Math.floor(Date.now() / 1e3);
57
+ const expiresAt = createdAt + ttl;
58
+ const message = computeRpSignatureMessage(nonceBytes, createdAt, expiresAt);
59
+ const msgHash = keccak_256(message);
60
+ const recSig = sign(msgHash, privKey);
61
+ const compact = recSig.toCompactRawBytes();
62
+ const sig65 = new Uint8Array(65);
63
+ sig65.set(compact, 0);
64
+ sig65[64] = recSig.recovery + 27;
65
+ return {
66
+ sig: "0x" + bytesToHex(sig65),
67
+ nonce: "0x" + bytesToHex(nonceBytes),
68
+ createdAt,
69
+ expiresAt
70
+ };
71
+ }
72
+
73
+ export { computeRpSignatureMessage, signRequest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@worldcoin/idkit-core",
3
- "version": "4.0.1-dev.2039000",
3
+ "version": "4.0.1-dev.370b7c0",
4
4
  "description": "Core IDKit SDK for World ID - Pure TypeScript, no dependencies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -11,6 +11,16 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
+ },
15
+ "./signing": {
16
+ "types": "./dist/signing.d.ts",
17
+ "import": "./dist/signing.js",
18
+ "require": "./dist/signing.cjs"
19
+ },
20
+ "./hashing": {
21
+ "types": "./dist/hashing.d.ts",
22
+ "import": "./dist/hashing.js",
23
+ "require": "./dist/hashing.cjs"
14
24
  }
15
25
  },
16
26
  "files": [
@@ -40,6 +50,10 @@
40
50
  "type": "git",
41
51
  "url": "https://github.com/worldcoin/idkit"
42
52
  },
53
+ "dependencies": {
54
+ "@noble/hashes": "^1.7.2",
55
+ "@noble/secp256k1": "^2.2.3"
56
+ },
43
57
  "devDependencies": {
44
58
  "@types/node": "^20.19.30",
45
59
  "@vitest/coverage-v8": "^4.0.18",