@taceo/oprf-client 0.8.0 → 0.9.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 +8 -9
- package/dist/index.cjs +17 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -6
- package/dist/index.d.ts +2 -6
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Services must be pre-built WebSocket URLs. Use `toOprfUri` to construct them fro
|
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
import { distributedOprf, toOprfUri } from '@taceo/oprf-client';
|
|
21
|
+
import { randomBlindingFactor } from '@taceo/oprf-core';
|
|
21
22
|
|
|
22
23
|
const bases = [
|
|
23
24
|
'http://node1.example.com',
|
|
@@ -27,12 +28,14 @@ const bases = [
|
|
|
27
28
|
|
|
28
29
|
const services = bases.map((s) => toOprfUri(s, 'my-module'));
|
|
29
30
|
|
|
31
|
+
const blindingFactor = randomBlindingFactor();
|
|
30
32
|
const result = await distributedOprf(
|
|
31
33
|
services,
|
|
32
34
|
2, // threshold
|
|
33
35
|
12345n, // query
|
|
36
|
+
blindingFactor,
|
|
34
37
|
0n, // domain separator
|
|
35
|
-
{
|
|
38
|
+
{ api_key: 'secret' } // auth (optional)
|
|
36
39
|
);
|
|
37
40
|
|
|
38
41
|
console.log(result.output); // OPRF output (bigint)
|
|
@@ -109,13 +112,13 @@ const output = finalizeOutput(domainSeparator, query, unblinded);
|
|
|
109
112
|
|
|
110
113
|
### Main Functions
|
|
111
114
|
|
|
112
|
-
- **`distributedOprf(services, threshold, query, domainSeparator,
|
|
115
|
+
- **`distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth?): Promise<VerifiableOprfOutput>`**
|
|
113
116
|
|
|
114
|
-
End-to-end distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize. Services must be pre-built WS URLs (use `toOprfUri`).
|
|
117
|
+
End-to-end distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize. Services must be pre-built WS URLs (use `toOprfUri`). The caller must provide a `blindingFactor` (use `randomBlindingFactor()` from `@taceo/oprf-core`).
|
|
115
118
|
|
|
116
|
-
- **`toOprfUri(service,
|
|
119
|
+
- **`toOprfUri(service, authModuleName, clientVersion?): string`**
|
|
117
120
|
|
|
118
|
-
Build a WebSocket URL for a single OPRF service. Converts `http://` → `ws://`, `https://` → `wss://`. Appends `/api/{
|
|
121
|
+
Build a WebSocket URL for a single OPRF service. Converts `http://` → `ws://`, `https://` → `wss://`. Appends `/api/{authModuleName}/oprf?version={clientVersion}`.
|
|
119
122
|
|
|
120
123
|
- **`initSessions(services, threshold, request): Promise<OprfSessions>`**
|
|
121
124
|
|
|
@@ -149,10 +152,6 @@ interface VerifiableOprfOutput {
|
|
|
149
152
|
oprfPublicKey: AffinePoint<bigint>; // Service public key
|
|
150
153
|
epoch: number; // Key epoch
|
|
151
154
|
}
|
|
152
|
-
|
|
153
|
-
interface DistributedOprfOptions<Auth = unknown> {
|
|
154
|
-
auth?: Auth; // Auth payload for OprfRequest
|
|
155
|
-
}
|
|
156
155
|
```
|
|
157
156
|
|
|
158
157
|
### Error Handling
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
|
|
38
38
|
// src/client.ts
|
|
39
39
|
var import_oprf_core = require("@taceo/oprf-core");
|
|
40
|
+
var import_utils = require("@noble/hashes/utils.js");
|
|
40
41
|
|
|
41
42
|
// src/errors.ts
|
|
42
43
|
var ServiceError = class _ServiceError extends Error {
|
|
@@ -213,7 +214,7 @@ var WebSocketSession = class _WebSocketSession {
|
|
|
213
214
|
reject(
|
|
214
215
|
new NodeError("WsError", {
|
|
215
216
|
reason: `Failed to connect to ${url}`,
|
|
216
|
-
cause: event instanceof ErrorEvent ? event.error : void 0
|
|
217
|
+
cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
|
|
217
218
|
})
|
|
218
219
|
);
|
|
219
220
|
};
|
|
@@ -282,7 +283,7 @@ var WebSocketSession = class _WebSocketSession {
|
|
|
282
283
|
reject(
|
|
283
284
|
new NodeError("WsError", {
|
|
284
285
|
reason: "WebSocket error",
|
|
285
|
-
cause: event instanceof ErrorEvent ? event.error : void 0
|
|
286
|
+
cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
|
|
286
287
|
})
|
|
287
288
|
);
|
|
288
289
|
};
|
|
@@ -460,14 +461,12 @@ function verifyDlogEquality(requestId, oprfPublicKey, blindedRequest, proofShare
|
|
|
460
461
|
}
|
|
461
462
|
return proof;
|
|
462
463
|
}
|
|
463
|
-
async function distributedOprf(services, threshold, query, domainSeparator,
|
|
464
|
+
async function distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth) {
|
|
464
465
|
if (new Set(services).size !== services.length) {
|
|
465
466
|
throw new OprfClientError("NonUniqueServices", "Services must be unique");
|
|
466
467
|
}
|
|
467
|
-
const blindingFactor = (0, import_oprf_core.randomBlindingFactor)();
|
|
468
468
|
const blindedRequest = (0, import_oprf_core.blindQuery)(query, blindingFactor);
|
|
469
|
-
const requestId =
|
|
470
|
-
const auth = options.auth ?? {};
|
|
469
|
+
const requestId = generateRequestId();
|
|
471
470
|
let sessions;
|
|
472
471
|
try {
|
|
473
472
|
sessions = await initSessions(services, threshold, {
|
|
@@ -526,6 +525,18 @@ async function distributedOprf(services, threshold, query, domainSeparator, opti
|
|
|
526
525
|
epoch: sessions.epoch
|
|
527
526
|
};
|
|
528
527
|
}
|
|
528
|
+
function generateRequestId() {
|
|
529
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
530
|
+
return globalThis.crypto.randomUUID();
|
|
531
|
+
}
|
|
532
|
+
const bytes = (0, import_utils.randomBytes)(16);
|
|
533
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
534
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
535
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
|
|
536
|
+
""
|
|
537
|
+
);
|
|
538
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
539
|
+
}
|
|
529
540
|
|
|
530
541
|
// src/uri.ts
|
|
531
542
|
function toOprfUri(service, auth_module_name, clientVersion = DEFAULT_CLIENT_VERSION) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * @taceo/oprf-client – WebSocket client for TACEO OPRF service.\n */\n\nexport {\n distributedOprf,\n generateChallengeRequest,\n verifyDlogEquality,\n type VerifiableOprfOutput,\n type OprfSessions,\n} from './client.js';\nexport { initSessions, finishSessions } from './sessions.js';\nexport type {\n OprfRequest,\n OprfResponse,\n OprfPublicKeyWithEpoch,\n} from './types.js';\nexport {\n OprfClientError,\n isOprfClientError,\n NodeError,\n isNodeError,\n ServiceError,\n aggregateError,\n} from './errors.js';\nexport type { OprfClientErrorCode, NodeErrorCode } from './errors.js';\nexport { toOprfUri } from './uri.js';\n","/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n randomBlindingFactor,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\nexport interface DistributedOprfOptions<Auth = unknown> {\n /** Auth payload sent in OprfRequest (must be JSON-serializable). */\n auth?: Auth;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n domainSeparator: bigint,\n options: DistributedOprfOptions = {}\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindingFactor: BlindingFactor = randomBlindingFactor();\n const blindedRequest = blindQuery(query, blindingFactor);\n\n const requestId = crypto.randomUUID();\n const auth = options.auth ?? ({} as unknown);\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * @taceo/oprf-client – WebSocket client for TACEO OPRF service.\n */\n\nexport {\n distributedOprf,\n generateChallengeRequest,\n verifyDlogEquality,\n type VerifiableOprfOutput,\n type OprfSessions,\n} from './client.js';\nexport { initSessions, finishSessions } from './sessions.js';\nexport type {\n OprfRequest,\n OprfResponse,\n OprfPublicKeyWithEpoch,\n} from './types.js';\nexport {\n OprfClientError,\n isOprfClientError,\n NodeError,\n isNodeError,\n ServiceError,\n aggregateError,\n} from './errors.js';\nexport type { OprfClientErrorCode, NodeErrorCode } from './errors.js';\nexport { toOprfUri } from './uri.js';\n","/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { randomBytes } from '@noble/hashes/utils.js';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n blindingFactor: BlindingFactor,\n domainSeparator: bigint,\n auth?: unknown\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindedRequest = blindQuery(query, blindingFactor);\n const requestId = generateRequestId();\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without randomUUID\n const bytes = randomBytes(16);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(\n ''\n );\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof } from '@taceo/oprf-core';
|
|
1
|
+
import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof, BlindingFactor } from '@taceo/oprf-core';
|
|
2
2
|
import { AffinePoint } from '@noble/curves/abstract/curve.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -134,15 +134,11 @@ declare function generateChallengeRequest(sessions: OprfSessions): DLogCommitmen
|
|
|
134
134
|
* Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.
|
|
135
135
|
*/
|
|
136
136
|
declare function verifyDlogEquality(requestId: string, oprfPublicKey: AffinePoint<bigint>, blindedRequest: AffinePoint<bigint>, proofShares: DLogProofShareShamir[], challenge: DLogCommitmentsShamir): DLogEqualityProof;
|
|
137
|
-
interface DistributedOprfOptions<Auth = unknown> {
|
|
138
|
-
/** Auth payload sent in OprfRequest (must be JSON-serializable). */
|
|
139
|
-
auth?: Auth;
|
|
140
|
-
}
|
|
141
137
|
/**
|
|
142
138
|
* Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.
|
|
143
139
|
* Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).
|
|
144
140
|
*/
|
|
145
|
-
declare function distributedOprf(services: string[], threshold: number, query: bigint, domainSeparator: bigint,
|
|
141
|
+
declare function distributedOprf(services: string[], threshold: number, query: bigint, blindingFactor: BlindingFactor, domainSeparator: bigint, auth?: unknown): Promise<VerifiableOprfOutput>;
|
|
146
142
|
|
|
147
143
|
/**
|
|
148
144
|
* Client error types matching the Rust oprf-client Error enum.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof } from '@taceo/oprf-core';
|
|
1
|
+
import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof, BlindingFactor } from '@taceo/oprf-core';
|
|
2
2
|
import { AffinePoint } from '@noble/curves/abstract/curve.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -134,15 +134,11 @@ declare function generateChallengeRequest(sessions: OprfSessions): DLogCommitmen
|
|
|
134
134
|
* Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.
|
|
135
135
|
*/
|
|
136
136
|
declare function verifyDlogEquality(requestId: string, oprfPublicKey: AffinePoint<bigint>, blindedRequest: AffinePoint<bigint>, proofShares: DLogProofShareShamir[], challenge: DLogCommitmentsShamir): DLogEqualityProof;
|
|
137
|
-
interface DistributedOprfOptions<Auth = unknown> {
|
|
138
|
-
/** Auth payload sent in OprfRequest (must be JSON-serializable). */
|
|
139
|
-
auth?: Auth;
|
|
140
|
-
}
|
|
141
137
|
/**
|
|
142
138
|
* Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.
|
|
143
139
|
* Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).
|
|
144
140
|
*/
|
|
145
|
-
declare function distributedOprf(services: string[], threshold: number, query: bigint, domainSeparator: bigint,
|
|
141
|
+
declare function distributedOprf(services: string[], threshold: number, query: bigint, blindingFactor: BlindingFactor, domainSeparator: bigint, auth?: unknown): Promise<VerifiableOprfOutput>;
|
|
146
142
|
|
|
147
143
|
/**
|
|
148
144
|
* Client error types matching the Rust oprf-client Error enum.
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
blindQuery,
|
|
4
4
|
unblindResponse,
|
|
5
5
|
finalizeOutput,
|
|
6
|
-
randomBlindingFactor,
|
|
7
6
|
prepareBlindingFactor,
|
|
8
7
|
DLogCommitmentsShamir,
|
|
9
8
|
dlogEqualityVerify,
|
|
10
9
|
BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE
|
|
11
10
|
} from "@taceo/oprf-core";
|
|
11
|
+
import { randomBytes } from "@noble/hashes/utils.js";
|
|
12
12
|
|
|
13
13
|
// src/errors.ts
|
|
14
14
|
var ServiceError = class _ServiceError extends Error {
|
|
@@ -185,7 +185,7 @@ var WebSocketSession = class _WebSocketSession {
|
|
|
185
185
|
reject(
|
|
186
186
|
new NodeError("WsError", {
|
|
187
187
|
reason: `Failed to connect to ${url}`,
|
|
188
|
-
cause: event instanceof ErrorEvent ? event.error : void 0
|
|
188
|
+
cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
|
|
189
189
|
})
|
|
190
190
|
);
|
|
191
191
|
};
|
|
@@ -254,7 +254,7 @@ var WebSocketSession = class _WebSocketSession {
|
|
|
254
254
|
reject(
|
|
255
255
|
new NodeError("WsError", {
|
|
256
256
|
reason: "WebSocket error",
|
|
257
|
-
cause: event instanceof ErrorEvent ? event.error : void 0
|
|
257
|
+
cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
|
|
258
258
|
})
|
|
259
259
|
);
|
|
260
260
|
};
|
|
@@ -432,14 +432,12 @@ function verifyDlogEquality(requestId, oprfPublicKey, blindedRequest, proofShare
|
|
|
432
432
|
}
|
|
433
433
|
return proof;
|
|
434
434
|
}
|
|
435
|
-
async function distributedOprf(services, threshold, query, domainSeparator,
|
|
435
|
+
async function distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth) {
|
|
436
436
|
if (new Set(services).size !== services.length) {
|
|
437
437
|
throw new OprfClientError("NonUniqueServices", "Services must be unique");
|
|
438
438
|
}
|
|
439
|
-
const blindingFactor = randomBlindingFactor();
|
|
440
439
|
const blindedRequest = blindQuery(query, blindingFactor);
|
|
441
|
-
const requestId =
|
|
442
|
-
const auth = options.auth ?? {};
|
|
440
|
+
const requestId = generateRequestId();
|
|
443
441
|
let sessions;
|
|
444
442
|
try {
|
|
445
443
|
sessions = await initSessions(services, threshold, {
|
|
@@ -498,6 +496,18 @@ async function distributedOprf(services, threshold, query, domainSeparator, opti
|
|
|
498
496
|
epoch: sessions.epoch
|
|
499
497
|
};
|
|
500
498
|
}
|
|
499
|
+
function generateRequestId() {
|
|
500
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
501
|
+
return globalThis.crypto.randomUUID();
|
|
502
|
+
}
|
|
503
|
+
const bytes = randomBytes(16);
|
|
504
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
505
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
506
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
|
|
507
|
+
""
|
|
508
|
+
);
|
|
509
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
510
|
+
}
|
|
501
511
|
|
|
502
512
|
// src/uri.ts
|
|
503
513
|
function toOprfUri(service, auth_module_name, clientVersion = DEFAULT_CLIENT_VERSION) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n randomBlindingFactor,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\nexport interface DistributedOprfOptions<Auth = unknown> {\n /** Auth payload sent in OprfRequest (must be JSON-serializable). */\n auth?: Auth;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n domainSeparator: bigint,\n options: DistributedOprfOptions = {}\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindingFactor: BlindingFactor = randomBlindingFactor();\n const blindedRequest = blindQuery(query, blindingFactor);\n\n const requestId = crypto.randomUUID();\n const auth = options.auth ?? ({} as unknown);\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { randomBytes } from '@noble/hashes/utils.js';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n blindingFactor: BlindingFactor,\n domainSeparator: bigint,\n auth?: unknown\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindedRequest = blindQuery(query, blindingFactor);\n const requestId = generateRequestId();\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without randomUUID\n const bytes = randomBytes(16);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(\n ''\n );\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taceo/oprf-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "WebSocket client for distributed threshold OPRF over a network of service nodes",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@noble/curves": "^2.0.1",
|
|
48
|
+
"@noble/hashes": "^2.0.1",
|
|
48
49
|
"@taceo/oprf-core": "0.4.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|