@psavelis/enterprise-blockchain 0.1.0 → 1.1.1
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 +15 -2
- package/dist/aid-settlement/application/reconciler.d.ts +13 -0
- package/dist/aid-settlement/application/reconciler.d.ts.map +1 -0
- package/dist/aid-settlement/application/reconciler.js +77 -0
- package/dist/aid-settlement/domain/entities.d.ts +24 -0
- package/dist/aid-settlement/domain/entities.d.ts.map +1 -0
- package/dist/aid-settlement/domain/entities.js +1 -0
- package/dist/aid-settlement/domain/ports.d.ts +10 -0
- package/dist/aid-settlement/domain/ports.d.ts.map +1 -0
- package/dist/aid-settlement/domain/ports.js +1 -0
- package/dist/aid-settlement/index.d.ts +19 -0
- package/dist/aid-settlement/index.d.ts.map +1 -0
- package/dist/aid-settlement/index.js +23 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts +12 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.js +17 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts +10 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts.map +1 -0
- package/dist/credentialing/application/clearance-evaluator.js +63 -0
- package/dist/credentialing/domain/entities.d.ts +28 -0
- package/dist/credentialing/domain/entities.d.ts.map +1 -0
- package/dist/credentialing/domain/entities.js +1 -0
- package/dist/credentialing/domain/ports.d.ts +9 -0
- package/dist/credentialing/domain/ports.d.ts.map +1 -0
- package/dist/credentialing/domain/ports.js +1 -0
- package/dist/credentialing/index.d.ts +19 -0
- package/dist/credentialing/index.d.ts.map +1 -0
- package/dist/credentialing/index.js +23 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts +11 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/credentialing/infrastructure/in-memory-store.js +14 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts +23 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/asymmetric-key-service.js +109 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts +18 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts.map +1 -0
- package/dist/hsm/application/envelope-encryption-service.js +59 -0
- package/dist/hsm/application/symmetric-key-service.d.ts +34 -0
- package/dist/hsm/application/symmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/symmetric-key-service.js +107 -0
- package/dist/hsm/domain/entities.d.ts +104 -0
- package/dist/hsm/domain/entities.d.ts.map +1 -0
- package/dist/hsm/domain/entities.js +10 -0
- package/dist/hsm/domain/ports.d.ts +20 -0
- package/dist/hsm/domain/ports.d.ts.map +1 -0
- package/dist/hsm/domain/ports.js +1 -0
- package/dist/hsm/index.d.ts +48 -0
- package/dist/hsm/index.d.ts.map +1 -0
- package/dist/hsm/index.js +97 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts +59 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log-factory.js +95 -0
- package/dist/hsm/infrastructure/audit-log.d.ts +8 -0
- package/dist/hsm/infrastructure/audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log.js +18 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts +55 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/file-audit-log.js +128 -0
- package/dist/hsm/infrastructure/key-store.d.ts +9 -0
- package/dist/hsm/infrastructure/key-store.d.ts.map +1 -0
- package/dist/hsm/infrastructure/key-store.js +12 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts +64 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/syslog-audit-log.js +167 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/integrations/besu-client/error-mapper.d.ts +9 -0
- package/dist/integrations/besu-client/error-mapper.d.ts.map +1 -0
- package/dist/integrations/besu-client/error-mapper.js +22 -0
- package/dist/integrations/besu-client/index.d.ts +65 -0
- package/dist/integrations/besu-client/index.d.ts.map +1 -0
- package/dist/integrations/besu-client/index.js +276 -0
- package/dist/integrations/besu-client/ports.d.ts +44 -0
- package/dist/integrations/besu-client/ports.d.ts.map +1 -0
- package/dist/integrations/besu-client/ports.js +1 -0
- package/dist/integrations/corda-gateway/index.d.ts +37 -0
- package/dist/integrations/corda-gateway/index.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/index.js +234 -0
- package/dist/integrations/corda-gateway/ports.d.ts +33 -0
- package/dist/integrations/corda-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/ports.js +1 -0
- package/dist/integrations/fabric-gateway/index.d.ts +78 -0
- package/dist/integrations/fabric-gateway/index.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/index.js +214 -0
- package/dist/integrations/fabric-gateway/ports.d.ts +50 -0
- package/dist/integrations/fabric-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/ports.js +1 -0
- package/dist/integrations/index.d.ts +19 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +19 -0
- package/dist/integrations/shared/env.d.ts +4 -0
- package/dist/integrations/shared/env.d.ts.map +1 -0
- package/dist/integrations/shared/env.js +24 -0
- package/dist/integrations/shared/retry.d.ts +79 -0
- package/dist/integrations/shared/retry.d.ts.map +1 -0
- package/dist/integrations/shared/retry.js +315 -0
- package/dist/mpc/adapters.d.ts +36 -0
- package/dist/mpc/adapters.d.ts.map +1 -0
- package/dist/mpc/adapters.js +46 -0
- package/dist/mpc/crypto.d.ts +2 -0
- package/dist/mpc/crypto.d.ts.map +1 -0
- package/dist/mpc/crypto.js +2 -0
- package/dist/mpc/dsa.d.ts +134 -0
- package/dist/mpc/dsa.d.ts.map +1 -0
- package/dist/mpc/dsa.js +127 -0
- package/dist/mpc/field.d.ts +127 -0
- package/dist/mpc/field.d.ts.map +1 -0
- package/dist/mpc/field.js +209 -0
- package/dist/mpc/hybrid-kem.d.ts +96 -0
- package/dist/mpc/hybrid-kem.d.ts.map +1 -0
- package/dist/mpc/hybrid-kem.js +136 -0
- package/dist/mpc/index.d.ts +135 -0
- package/dist/mpc/index.d.ts.map +1 -0
- package/dist/mpc/index.js +348 -0
- package/dist/mpc/kyber.d.ts +134 -0
- package/dist/mpc/kyber.d.ts.map +1 -0
- package/dist/mpc/kyber.js +143 -0
- package/dist/mpc/ports.d.ts +67 -0
- package/dist/mpc/ports.d.ts.map +1 -0
- package/dist/mpc/ports.js +9 -0
- package/dist/mpc/quantum.d.ts +80 -0
- package/dist/mpc/quantum.d.ts.map +1 -0
- package/dist/mpc/quantum.js +180 -0
- package/dist/p2mr/adapters.d.ts +31 -0
- package/dist/p2mr/adapters.d.ts.map +1 -0
- package/dist/p2mr/adapters.js +35 -0
- package/dist/p2mr/index.d.ts +63 -0
- package/dist/p2mr/index.d.ts.map +1 -0
- package/dist/p2mr/index.js +59 -0
- package/dist/p2mr/merkle-tree.d.ts +109 -0
- package/dist/p2mr/merkle-tree.d.ts.map +1 -0
- package/dist/p2mr/merkle-tree.js +239 -0
- package/dist/p2mr/p2mr-output.d.ts +142 -0
- package/dist/p2mr/p2mr-output.d.ts.map +1 -0
- package/dist/p2mr/p2mr-output.js +150 -0
- package/dist/p2mr/ports.d.ts +52 -0
- package/dist/p2mr/ports.d.ts.map +1 -0
- package/dist/p2mr/ports.js +9 -0
- package/dist/p2mr/script-interpreter.d.ts +92 -0
- package/dist/p2mr/script-interpreter.d.ts.map +1 -0
- package/dist/p2mr/script-interpreter.js +535 -0
- package/dist/p2mr/script-leaf.d.ts +70 -0
- package/dist/p2mr/script-leaf.d.ts.map +1 -0
- package/dist/p2mr/script-leaf.js +203 -0
- package/dist/p2mr/spend-proof.d.ts +95 -0
- package/dist/p2mr/spend-proof.d.ts.map +1 -0
- package/dist/p2mr/spend-proof.js +358 -0
- package/dist/p2mr/types.d.ts +209 -0
- package/dist/p2mr/types.d.ts.map +1 -0
- package/dist/p2mr/types.js +9 -0
- package/dist/privacy/application/view-projector.d.ts +13 -0
- package/dist/privacy/application/view-projector.d.ts.map +1 -0
- package/dist/privacy/application/view-projector.js +85 -0
- package/dist/privacy/domain/entities.d.ts +26 -0
- package/dist/privacy/domain/entities.d.ts.map +1 -0
- package/dist/privacy/domain/entities.js +1 -0
- package/dist/privacy/domain/ports.d.ts +7 -0
- package/dist/privacy/domain/ports.d.ts.map +1 -0
- package/dist/privacy/domain/ports.js +1 -0
- package/dist/privacy/index.d.ts +21 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +25 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts +8 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/privacy/infrastructure/in-memory-store.js +7 -0
- package/dist/protocols/besu-port.d.ts +80 -0
- package/dist/protocols/besu-port.d.ts.map +1 -0
- package/dist/protocols/besu-port.js +1 -0
- package/dist/protocols/corda-port.d.ts +103 -0
- package/dist/protocols/corda-port.d.ts.map +1 -0
- package/dist/protocols/corda-port.js +9 -0
- package/dist/protocols/credentialing-port.d.ts +11 -0
- package/dist/protocols/credentialing-port.d.ts.map +1 -0
- package/dist/protocols/credentialing-port.js +1 -0
- package/dist/protocols/fabric-port.d.ts +89 -0
- package/dist/protocols/fabric-port.d.ts.map +1 -0
- package/dist/protocols/fabric-port.js +9 -0
- package/dist/protocols/index.d.ts +14 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +7 -0
- package/dist/protocols/p2mr-port.d.ts +159 -0
- package/dist/protocols/p2mr-port.d.ts.map +1 -0
- package/dist/protocols/p2mr-port.js +12 -0
- package/dist/protocols/privacy-port.d.ts +9 -0
- package/dist/protocols/privacy-port.d.ts.map +1 -0
- package/dist/protocols/privacy-port.js +1 -0
- package/dist/protocols/traceability-port.d.ts +12 -0
- package/dist/protocols/traceability-port.d.ts.map +1 -0
- package/dist/protocols/traceability-port.js +1 -0
- package/dist/shared/collection-store.d.ts +12 -0
- package/dist/shared/collection-store.d.ts.map +1 -0
- package/dist/shared/collection-store.js +26 -0
- package/dist/shared/commit.d.ts +24 -0
- package/dist/shared/commit.d.ts.map +1 -0
- package/dist/shared/commit.js +50 -0
- package/dist/shared/crypto.d.ts +2 -0
- package/dist/shared/crypto.d.ts.map +1 -0
- package/dist/shared/crypto.js +4 -0
- package/dist/shared/date.d.ts +2 -0
- package/dist/shared/date.d.ts.map +1 -0
- package/dist/shared/date.js +3 -0
- package/dist/shared/index.d.ts +9 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +11 -0
- package/dist/shared/logger.d.ts +37 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +45 -0
- package/dist/shared/store.d.ts +25 -0
- package/dist/shared/store.d.ts.map +1 -0
- package/dist/shared/store.js +18 -0
- package/dist/shared/telemetry-sdk.d.ts +26 -0
- package/dist/shared/telemetry-sdk.d.ts.map +1 -0
- package/dist/shared/telemetry-sdk.js +97 -0
- package/dist/shared/telemetry.d.ts +86 -0
- package/dist/shared/telemetry.d.ts.map +1 -0
- package/dist/shared/telemetry.js +137 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts +112 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/aggregator-service.js +256 -0
- package/dist/stark-settlement/application/ledger-service.d.ts +114 -0
- package/dist/stark-settlement/application/ledger-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/ledger-service.js +318 -0
- package/dist/stark-settlement/application/settlement-service.d.ts +104 -0
- package/dist/stark-settlement/application/settlement-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/settlement-service.js +251 -0
- package/dist/stark-settlement/domain/entities.d.ts +365 -0
- package/dist/stark-settlement/domain/entities.d.ts.map +1 -0
- package/dist/stark-settlement/domain/entities.js +29 -0
- package/dist/stark-settlement/domain/ports.d.ts +485 -0
- package/dist/stark-settlement/domain/ports.d.ts.map +1 -0
- package/dist/stark-settlement/domain/ports.js +14 -0
- package/dist/stark-settlement/domain/value-objects.d.ts +268 -0
- package/dist/stark-settlement/domain/value-objects.d.ts.map +1 -0
- package/dist/stark-settlement/domain/value-objects.js +500 -0
- package/dist/stark-settlement/index.d.ts +172 -0
- package/dist/stark-settlement/index.d.ts.map +1 -0
- package/dist/stark-settlement/index.js +193 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts +52 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.js +154 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts +88 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.js +187 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts +59 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.js +85 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts +60 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.js +104 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts +115 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.js +191 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts +65 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.js +207 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts +73 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.js +287 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts +78 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.js +172 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts +56 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.js +261 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts +125 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.js +416 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts +68 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.js +238 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts +30 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.js +57 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts +45 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.js +171 -0
- package/dist/traceability/application/recall-assessor.d.ts +13 -0
- package/dist/traceability/application/recall-assessor.d.ts.map +1 -0
- package/dist/traceability/application/recall-assessor.js +74 -0
- package/dist/traceability/domain/entities.d.ts +23 -0
- package/dist/traceability/domain/entities.d.ts.map +1 -0
- package/dist/traceability/domain/entities.js +1 -0
- package/dist/traceability/domain/ports.d.ts +23 -0
- package/dist/traceability/domain/ports.d.ts.map +1 -0
- package/dist/traceability/domain/ports.js +1 -0
- package/dist/traceability/domain/recall.d.ts +12 -0
- package/dist/traceability/domain/recall.d.ts.map +1 -0
- package/dist/traceability/domain/recall.js +1 -0
- package/dist/traceability/index.d.ts +22 -0
- package/dist/traceability/index.d.ts.map +1 -0
- package/dist/traceability/index.js +26 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts +13 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/traceability/infrastructure/in-memory-store.js +24 -0
- package/package.json +12 -9
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared retry policy and circuit breaker for integration adapters.
|
|
3
|
+
*
|
|
4
|
+
* Each platform adapter configures which error codes are retryable:
|
|
5
|
+
* - Fabric: UNAVAILABLE, DEADLINE_EXCEEDED gRPC status codes
|
|
6
|
+
* - Besu: SERVER_ERROR, TIMEOUT JSON-RPC errors (not NONCE_TOO_LOW)
|
|
7
|
+
* - Corda: HTTP 502/503/504 (not 400/401/403)
|
|
8
|
+
*
|
|
9
|
+
* OpenTelemetry instrumentation:
|
|
10
|
+
* - withRetry() creates spans per attempt with error code attributes
|
|
11
|
+
* - CircuitBreaker emits state transition metrics
|
|
12
|
+
*/
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
import { createTracer, createMeter, TelemetryAttributes, SpanStatusCode, } from "../../shared/telemetry.js";
|
|
15
|
+
const tracer = createTracer("retry-policy");
|
|
16
|
+
const meter = createMeter("circuit-breaker");
|
|
17
|
+
// Metrics
|
|
18
|
+
const retryAttemptCounter = meter.createCounter("retry.attempts", {
|
|
19
|
+
description: "Number of retry attempts",
|
|
20
|
+
unit: "1",
|
|
21
|
+
});
|
|
22
|
+
const retrySuccessCounter = meter.createCounter("retry.successes", {
|
|
23
|
+
description: "Number of successful operations (with or without retries)",
|
|
24
|
+
unit: "1",
|
|
25
|
+
});
|
|
26
|
+
const retryFailureCounter = meter.createCounter("retry.failures", {
|
|
27
|
+
description: "Number of operations that failed after all retries",
|
|
28
|
+
unit: "1",
|
|
29
|
+
});
|
|
30
|
+
const circuitBreakerStateGauge = meter.createObservableGauge("circuit_breaker.state", {
|
|
31
|
+
description: "Current circuit breaker state (0=closed, 1=half-open, 2=open)",
|
|
32
|
+
unit: "1",
|
|
33
|
+
});
|
|
34
|
+
const circuitBreakerTransitions = meter.createCounter("circuit_breaker.transitions", {
|
|
35
|
+
description: "Number of circuit breaker state transitions",
|
|
36
|
+
unit: "1",
|
|
37
|
+
});
|
|
38
|
+
export const DEFAULT_RETRY_POLICY = {
|
|
39
|
+
maxAttempts: 3,
|
|
40
|
+
baseDelayMs: 500,
|
|
41
|
+
maxDelayMs: 10_000,
|
|
42
|
+
retryableErrors: [],
|
|
43
|
+
};
|
|
44
|
+
// ── Platform-specific retry policies ────────────────────────────────
|
|
45
|
+
export const FABRIC_RETRY_POLICY = {
|
|
46
|
+
maxAttempts: 4,
|
|
47
|
+
baseDelayMs: 500,
|
|
48
|
+
maxDelayMs: 8_000,
|
|
49
|
+
retryableErrors: ["UNAVAILABLE", "DEADLINE_EXCEEDED"],
|
|
50
|
+
};
|
|
51
|
+
export const BESU_RETRY_POLICY = {
|
|
52
|
+
maxAttempts: 3,
|
|
53
|
+
baseDelayMs: 1_000,
|
|
54
|
+
maxDelayMs: 15_000,
|
|
55
|
+
retryableErrors: ["SERVER_ERROR", "TIMEOUT"],
|
|
56
|
+
};
|
|
57
|
+
/** Non-retryable Besu errors (fail immediately). */
|
|
58
|
+
export const BESU_NON_RETRYABLE = ["NONCE_TOO_LOW", "INSUFFICIENT_FUNDS"];
|
|
59
|
+
export const CORDA_RETRY_POLICY = {
|
|
60
|
+
maxAttempts: 3,
|
|
61
|
+
baseDelayMs: 1_000,
|
|
62
|
+
maxDelayMs: 10_000,
|
|
63
|
+
retryableErrors: ["502", "503", "504", "TIMEOUT"],
|
|
64
|
+
};
|
|
65
|
+
/** Non-retryable Corda HTTP status codes. */
|
|
66
|
+
export const CORDA_NON_RETRYABLE = ["400", "401", "403"];
|
|
67
|
+
// ── Retry with exponential backoff ──────────────────────────────────
|
|
68
|
+
export function isRetryable(errorCode, policy, nonRetryable = []) {
|
|
69
|
+
if (nonRetryable.includes(errorCode))
|
|
70
|
+
return false;
|
|
71
|
+
return policy.retryableErrors.includes(errorCode);
|
|
72
|
+
}
|
|
73
|
+
export function computeDelay(attempt, policy) {
|
|
74
|
+
// Use cryptographically secure randomness to prevent timing attacks
|
|
75
|
+
// that could exploit predictable backoff patterns
|
|
76
|
+
const randomByte = randomBytes(1)[0];
|
|
77
|
+
const jitter = (randomByte % 30) / 100 + 0.85; // ±15%
|
|
78
|
+
const delay = Math.min(policy.baseDelayMs * Math.pow(2, attempt) * jitter, policy.maxDelayMs);
|
|
79
|
+
return Math.round(delay);
|
|
80
|
+
}
|
|
81
|
+
export async function withRetry(fn, policy, nonRetryable = [], extractErrorCode = defaultExtractErrorCode, operationName = "operation") {
|
|
82
|
+
if (policy.maxAttempts < 1) {
|
|
83
|
+
throw new Error("RetryPolicy.maxAttempts must be >= 1");
|
|
84
|
+
}
|
|
85
|
+
return tracer.startActiveSpan(`retry.${operationName}`, {
|
|
86
|
+
attributes: {
|
|
87
|
+
[TelemetryAttributes.RETRY_MAX_ATTEMPTS]: policy.maxAttempts,
|
|
88
|
+
},
|
|
89
|
+
}, async (parentSpan) => {
|
|
90
|
+
let lastError;
|
|
91
|
+
for (let attempt = 0; attempt < policy.maxAttempts; attempt++) {
|
|
92
|
+
const attemptSpan = tracer.startSpan(`retry.attempt`, {
|
|
93
|
+
attributes: {
|
|
94
|
+
[TelemetryAttributes.RETRY_ATTEMPT]: attempt + 1,
|
|
95
|
+
[TelemetryAttributes.RETRY_MAX_ATTEMPTS]: policy.maxAttempts,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
retryAttemptCounter.add(1, {
|
|
100
|
+
[TelemetryAttributes.RETRY_ATTEMPT]: attempt + 1,
|
|
101
|
+
});
|
|
102
|
+
const result = await fn();
|
|
103
|
+
attemptSpan.setStatus({ code: SpanStatusCode.OK });
|
|
104
|
+
attemptSpan.end();
|
|
105
|
+
parentSpan.setStatus({ code: SpanStatusCode.OK });
|
|
106
|
+
parentSpan.setAttribute("retry.total_attempts", attempt + 1);
|
|
107
|
+
parentSpan.end();
|
|
108
|
+
retrySuccessCounter.add(1, {
|
|
109
|
+
"retry.total_attempts": attempt + 1,
|
|
110
|
+
});
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
lastError = err;
|
|
115
|
+
const code = extractErrorCode(err);
|
|
116
|
+
attemptSpan.setAttribute(TelemetryAttributes.RETRY_ERROR_CODE, code);
|
|
117
|
+
attemptSpan.setStatus({
|
|
118
|
+
code: SpanStatusCode.ERROR,
|
|
119
|
+
message: err instanceof Error ? err.message : String(err),
|
|
120
|
+
});
|
|
121
|
+
if (err instanceof Error) {
|
|
122
|
+
attemptSpan.recordException(err);
|
|
123
|
+
}
|
|
124
|
+
attemptSpan.end();
|
|
125
|
+
if (!isRetryable(code, policy, nonRetryable)) {
|
|
126
|
+
parentSpan.setStatus({
|
|
127
|
+
code: SpanStatusCode.ERROR,
|
|
128
|
+
message: `Non-retryable error: ${code}`,
|
|
129
|
+
});
|
|
130
|
+
parentSpan.setAttribute("retry.total_attempts", attempt + 1);
|
|
131
|
+
parentSpan.setAttribute(TelemetryAttributes.RETRY_ERROR_CODE, code);
|
|
132
|
+
parentSpan.end();
|
|
133
|
+
retryFailureCounter.add(1, {
|
|
134
|
+
[TelemetryAttributes.RETRY_ERROR_CODE]: code,
|
|
135
|
+
"retry.retryable": false,
|
|
136
|
+
});
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
if (attempt < policy.maxAttempts - 1) {
|
|
140
|
+
const delay = computeDelay(attempt, policy);
|
|
141
|
+
parentSpan.addEvent("retry.backoff", {
|
|
142
|
+
[TelemetryAttributes.RETRY_DELAY_MS]: delay,
|
|
143
|
+
[TelemetryAttributes.RETRY_ATTEMPT]: attempt + 1,
|
|
144
|
+
});
|
|
145
|
+
await sleep(delay);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
parentSpan.setStatus({
|
|
150
|
+
code: SpanStatusCode.ERROR,
|
|
151
|
+
message: `All ${policy.maxAttempts} attempts exhausted`,
|
|
152
|
+
});
|
|
153
|
+
parentSpan.setAttribute("retry.total_attempts", policy.maxAttempts);
|
|
154
|
+
parentSpan.end();
|
|
155
|
+
retryFailureCounter.add(1, {
|
|
156
|
+
"retry.retryable": true,
|
|
157
|
+
"retry.exhausted": true,
|
|
158
|
+
});
|
|
159
|
+
throw lastError;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
export const DEFAULT_CIRCUIT_BREAKER_OPTIONS = {
|
|
163
|
+
failureThreshold: 5,
|
|
164
|
+
cooldownMs: 30_000,
|
|
165
|
+
};
|
|
166
|
+
export class CircuitBreaker {
|
|
167
|
+
state = "closed";
|
|
168
|
+
consecutiveFailures = 0;
|
|
169
|
+
lastFailureTime = 0;
|
|
170
|
+
options;
|
|
171
|
+
name;
|
|
172
|
+
constructor(options = {}, name = "default") {
|
|
173
|
+
this.options = { ...DEFAULT_CIRCUIT_BREAKER_OPTIONS, ...options };
|
|
174
|
+
this.name = name;
|
|
175
|
+
// Register observable gauge for this circuit breaker
|
|
176
|
+
circuitBreakerStateGauge.addCallback((result) => {
|
|
177
|
+
const stateValue = this.state === "closed" ? 0 : this.state === "half-open" ? 1 : 2;
|
|
178
|
+
result.observe(stateValue, {
|
|
179
|
+
"circuit_breaker.name": this.name,
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
getState() {
|
|
184
|
+
if (this.state === "open") {
|
|
185
|
+
const elapsed = Date.now() - this.lastFailureTime;
|
|
186
|
+
if (elapsed >= this.options.cooldownMs) {
|
|
187
|
+
this.transitionTo("half-open");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return this.state;
|
|
191
|
+
}
|
|
192
|
+
async execute(fn) {
|
|
193
|
+
const currentState = this.getState();
|
|
194
|
+
if (currentState === "open") {
|
|
195
|
+
throw new Error(`Circuit breaker is OPEN — requests blocked for ${this.options.cooldownMs}ms cooldown`);
|
|
196
|
+
}
|
|
197
|
+
// In half-open state, only allow one probe request through.
|
|
198
|
+
// Immediately transition to open so concurrent callers are blocked
|
|
199
|
+
// (prevents thundering herd after cooldown).
|
|
200
|
+
if (currentState === "half-open") {
|
|
201
|
+
this.transitionTo("open");
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const result = await fn();
|
|
205
|
+
this.onSuccess();
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
this.onFailure();
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
transitionTo(newState) {
|
|
214
|
+
if (this.state !== newState) {
|
|
215
|
+
const previousState = this.state;
|
|
216
|
+
this.state = newState;
|
|
217
|
+
circuitBreakerTransitions.add(1, {
|
|
218
|
+
"circuit_breaker.name": this.name,
|
|
219
|
+
"circuit_breaker.from_state": previousState,
|
|
220
|
+
"circuit_breaker.to_state": newState,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
onSuccess() {
|
|
225
|
+
this.consecutiveFailures = 0;
|
|
226
|
+
this.transitionTo("closed");
|
|
227
|
+
}
|
|
228
|
+
onFailure() {
|
|
229
|
+
this.consecutiveFailures++;
|
|
230
|
+
this.lastFailureTime = Date.now();
|
|
231
|
+
if (this.consecutiveFailures >= this.options.failureThreshold) {
|
|
232
|
+
this.transitionTo("open");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** Reset the circuit to closed state. Useful for testing. */
|
|
236
|
+
reset() {
|
|
237
|
+
this.transitionTo("closed");
|
|
238
|
+
this.consecutiveFailures = 0;
|
|
239
|
+
this.lastFailureTime = 0;
|
|
240
|
+
}
|
|
241
|
+
/** Get health status for Kubernetes probes or monitoring. */
|
|
242
|
+
getHealthStatus() {
|
|
243
|
+
const currentState = this.getState();
|
|
244
|
+
return {
|
|
245
|
+
state: currentState,
|
|
246
|
+
healthy: currentState === "closed",
|
|
247
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
248
|
+
lastFailureTime: this.lastFailureTime > 0
|
|
249
|
+
? new Date(this.lastFailureTime).toISOString()
|
|
250
|
+
: null,
|
|
251
|
+
cooldownRemainingMs: currentState === "open"
|
|
252
|
+
? Math.max(0, this.options.cooldownMs - (Date.now() - this.lastFailureTime))
|
|
253
|
+
: 0,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Health checker for integration clients.
|
|
259
|
+
* Combines circuit breaker state with active endpoint probing.
|
|
260
|
+
*/
|
|
261
|
+
export class IntegrationHealthChecker {
|
|
262
|
+
name;
|
|
263
|
+
circuitBreaker;
|
|
264
|
+
probe;
|
|
265
|
+
constructor(name, circuitBreaker, probe) {
|
|
266
|
+
this.name = name;
|
|
267
|
+
this.circuitBreaker = circuitBreaker;
|
|
268
|
+
this.probe = probe;
|
|
269
|
+
}
|
|
270
|
+
async check() {
|
|
271
|
+
const circuitHealth = this.circuitBreaker.getHealthStatus();
|
|
272
|
+
const status = {
|
|
273
|
+
name: this.name,
|
|
274
|
+
healthy: circuitHealth.healthy,
|
|
275
|
+
circuitBreaker: circuitHealth,
|
|
276
|
+
lastCheckTime: new Date().toISOString(),
|
|
277
|
+
};
|
|
278
|
+
// If circuit is open, don't probe (would fail anyway)
|
|
279
|
+
if (!circuitHealth.healthy || !this.probe) {
|
|
280
|
+
return status;
|
|
281
|
+
}
|
|
282
|
+
// Active probe to verify endpoint is reachable
|
|
283
|
+
const start = Date.now();
|
|
284
|
+
try {
|
|
285
|
+
await this.probe();
|
|
286
|
+
status.latencyMs = Date.now() - start;
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
status.healthy = false;
|
|
290
|
+
status.latencyMs = Date.now() - start;
|
|
291
|
+
status.error =
|
|
292
|
+
err instanceof Error ? err.message : "Unknown probe failure";
|
|
293
|
+
}
|
|
294
|
+
return status;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
298
|
+
function defaultExtractErrorCode(err) {
|
|
299
|
+
if (err && typeof err === "object") {
|
|
300
|
+
const e = err;
|
|
301
|
+
if (typeof e.code === "string")
|
|
302
|
+
return e.code;
|
|
303
|
+
// gRPC-js ServiceError.code is a number (e.g., 14 for UNAVAILABLE)
|
|
304
|
+
if (typeof e.code === "number")
|
|
305
|
+
return String(e.code);
|
|
306
|
+
if (typeof e.status === "number")
|
|
307
|
+
return String(e.status);
|
|
308
|
+
if (typeof e.statusCode === "number")
|
|
309
|
+
return String(e.statusCode);
|
|
310
|
+
}
|
|
311
|
+
return "UNKNOWN";
|
|
312
|
+
}
|
|
313
|
+
function sleep(ms) {
|
|
314
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
315
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPC Module Infrastructure Adapters
|
|
3
|
+
*
|
|
4
|
+
* These adapters implement the ports defined in ports.ts using Node.js crypto.
|
|
5
|
+
* They live in the infrastructure layer and are injected into domain services.
|
|
6
|
+
*
|
|
7
|
+
* @see modules/mpc/src/ports.ts
|
|
8
|
+
*/
|
|
9
|
+
import type { RandomnessProvider, CommitmentProvider } from "./ports.js";
|
|
10
|
+
/**
|
|
11
|
+
* Node.js crypto implementation of RandomnessProvider.
|
|
12
|
+
*
|
|
13
|
+
* Uses Node.js crypto module for cryptographically secure randomness.
|
|
14
|
+
* This is the default adapter for production use.
|
|
15
|
+
*/
|
|
16
|
+
export declare class NodeRandomnessProvider implements RandomnessProvider {
|
|
17
|
+
randomBytes(length: number): Buffer;
|
|
18
|
+
randomInt(min: number, max: number): number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Node.js crypto implementation of CommitmentProvider.
|
|
22
|
+
*
|
|
23
|
+
* Uses SHA-256 for commitments and timing-safe comparison.
|
|
24
|
+
*/
|
|
25
|
+
export declare class NodeCommitmentProvider implements CommitmentProvider {
|
|
26
|
+
commitShare(partyId: string, shareIndex: number, value: number | bigint, nonce: string): string;
|
|
27
|
+
sha256hex(data: string): string;
|
|
28
|
+
timingSafeCompare(a: string, b: string): boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Default instances for convenience.
|
|
32
|
+
* Domain code should prefer dependency injection over these singletons.
|
|
33
|
+
*/
|
|
34
|
+
export declare const defaultRandomnessProvider: NodeRandomnessProvider;
|
|
35
|
+
export declare const defaultCommitmentProvider: NodeCommitmentProvider;
|
|
36
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../../src/mpc/adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGzE;;;;;GAKG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAInC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;CAG5C;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,WAAW,CACT,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,MAAM;IAIT,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI/B,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAGjD;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,wBAA+B,CAAC;AACtE,eAAO,MAAM,yBAAyB,wBAA+B,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPC Module Infrastructure Adapters
|
|
3
|
+
*
|
|
4
|
+
* These adapters implement the ports defined in ports.ts using Node.js crypto.
|
|
5
|
+
* They live in the infrastructure layer and are injected into domain services.
|
|
6
|
+
*
|
|
7
|
+
* @see modules/mpc/src/ports.ts
|
|
8
|
+
*/
|
|
9
|
+
import { randomBytes as nodeRandomBytes, randomInt as nodeRandomInt, } from "node:crypto";
|
|
10
|
+
import { commitShare, sha256hex, timingSafeCompare } from "./crypto.js";
|
|
11
|
+
/**
|
|
12
|
+
* Node.js crypto implementation of RandomnessProvider.
|
|
13
|
+
*
|
|
14
|
+
* Uses Node.js crypto module for cryptographically secure randomness.
|
|
15
|
+
* This is the default adapter for production use.
|
|
16
|
+
*/
|
|
17
|
+
export class NodeRandomnessProvider {
|
|
18
|
+
randomBytes(length) {
|
|
19
|
+
return nodeRandomBytes(length);
|
|
20
|
+
}
|
|
21
|
+
randomInt(min, max) {
|
|
22
|
+
return nodeRandomInt(min, max);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Node.js crypto implementation of CommitmentProvider.
|
|
27
|
+
*
|
|
28
|
+
* Uses SHA-256 for commitments and timing-safe comparison.
|
|
29
|
+
*/
|
|
30
|
+
export class NodeCommitmentProvider {
|
|
31
|
+
commitShare(partyId, shareIndex, value, nonce) {
|
|
32
|
+
return commitShare(partyId, shareIndex, value, nonce);
|
|
33
|
+
}
|
|
34
|
+
sha256hex(data) {
|
|
35
|
+
return sha256hex(data);
|
|
36
|
+
}
|
|
37
|
+
timingSafeCompare(a, b) {
|
|
38
|
+
return timingSafeCompare(a, b);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Default instances for convenience.
|
|
43
|
+
* Domain code should prefer dependency injection over these singletons.
|
|
44
|
+
*/
|
|
45
|
+
export const defaultRandomnessProvider = new NodeRandomnessProvider();
|
|
46
|
+
export const defaultCommitmentProvider = new NodeCommitmentProvider();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/mpc/crypto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ML-DSA (Dilithium) — Digital Signature Algorithm
|
|
3
|
+
*
|
|
4
|
+
* Classical signature schemes (ECDSA, RSA) rely on the hardness of the
|
|
5
|
+
* discrete logarithm and integer factorisation problems. Shor's algorithm,
|
|
6
|
+
* running on a sufficiently large cryptographically-relevant quantum computer
|
|
7
|
+
* (CRQC), solves both in polynomial time — meaning historical signatures could
|
|
8
|
+
* be forged retroactively ("harvest-now, forge-later" threat to long-lived
|
|
9
|
+
* documents and trade rails).
|
|
10
|
+
*
|
|
11
|
+
* ML-DSA is the NIST-standardised Digital Signature Algorithm designed to
|
|
12
|
+
* replace ECDSA. It is based on the Module-Lattice problem (MLWE / MSIS),
|
|
13
|
+
* which has no known quantum speedup beyond Grover's algorithm.
|
|
14
|
+
*
|
|
15
|
+
* Standard: NIST FIPS 204 (finalised August 2024)
|
|
16
|
+
* Reference: https://csrc.nist.gov/pubs/fips/204/final
|
|
17
|
+
*
|
|
18
|
+
* Noble API note (differs from ML-KEM):
|
|
19
|
+
* sign(message, secretKey) — message is the FIRST argument
|
|
20
|
+
* verify(signature, message, publicKey)
|
|
21
|
+
*
|
|
22
|
+
* This module wraps @noble/post-quantum, a pure-TypeScript implementation with
|
|
23
|
+
* zero native-code dependencies, which makes it platform-portable and easy to
|
|
24
|
+
* audit.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* ML-DSA parameter sets standardised in FIPS 204.
|
|
28
|
+
*
|
|
29
|
+
* The number (44 / 65 / 87) refers to the module dimension:
|
|
30
|
+
* - 44 → NIST Level 2 (~AES-128, comparable to P-256/ES256)
|
|
31
|
+
* - 65 → NIST Level 3 (~AES-192, comparable to P-384/ES384)
|
|
32
|
+
* - 87 → NIST Level 5 (~AES-256, comparable to P-521/ES512)
|
|
33
|
+
*
|
|
34
|
+
* NIST recommends ML-DSA-65 as the general-purpose choice for new systems.
|
|
35
|
+
* ML-DSA-87 is appropriate for root CAs and long-lived documents (30+ years).
|
|
36
|
+
*/
|
|
37
|
+
export type MlDsaParams = "ml-dsa-44" | "ml-dsa-65" | "ml-dsa-87";
|
|
38
|
+
/**
|
|
39
|
+
* Public key, secret key, and signature lengths (in bytes) for each
|
|
40
|
+
* parameter set. Useful for validation and test assertions.
|
|
41
|
+
*
|
|
42
|
+
* Note: Noble's secretKey representation includes expanded key material;
|
|
43
|
+
* hence sk lengths are larger than the FIPS 204 §7 algebraic key sizes.
|
|
44
|
+
*/
|
|
45
|
+
export declare const ML_DSA_SIZES: Record<MlDsaParams, {
|
|
46
|
+
publicKey: number;
|
|
47
|
+
secretKey: number;
|
|
48
|
+
signature: number;
|
|
49
|
+
}>;
|
|
50
|
+
export interface DsaKeyPair {
|
|
51
|
+
publicKey: Uint8Array;
|
|
52
|
+
secretKey: Uint8Array;
|
|
53
|
+
/** Which ML-DSA parameter set this keypair belongs to. */
|
|
54
|
+
params: MlDsaParams;
|
|
55
|
+
}
|
|
56
|
+
export interface DsaSignatureResult {
|
|
57
|
+
/** The raw ML-DSA signature bytes to send alongside the message. */
|
|
58
|
+
signature: Uint8Array;
|
|
59
|
+
/**
|
|
60
|
+
* SHA-256 hex digest of the signature's hex encoding.
|
|
61
|
+
*
|
|
62
|
+
* Computed as: sha256hex(Buffer.from(signature).toString("hex"))
|
|
63
|
+
*
|
|
64
|
+
* This means the hash is over the hex-encoded string representation of the
|
|
65
|
+
* signature bytes, not the raw bytes directly. Suitable for on-chain
|
|
66
|
+
* commitments or audit logs.
|
|
67
|
+
*/
|
|
68
|
+
auditCommitment: string;
|
|
69
|
+
}
|
|
70
|
+
export interface DsaAuditRecord {
|
|
71
|
+
params: MlDsaParams;
|
|
72
|
+
/**
|
|
73
|
+
* SHA-256 of the public key's hex encoding — stable identifier for the signer's keypair.
|
|
74
|
+
* Computed as: sha256hex(Buffer.from(publicKey).toString("hex"))
|
|
75
|
+
*/
|
|
76
|
+
publicKeyHash: string;
|
|
77
|
+
/**
|
|
78
|
+
* SHA-256 of the signature's hex encoding — unique per signing event.
|
|
79
|
+
* Same as auditCommitment from DsaSignatureResult.
|
|
80
|
+
*/
|
|
81
|
+
signatureHash: string;
|
|
82
|
+
/**
|
|
83
|
+
* SHA-256 of the message's hex encoding — proves which message was signed.
|
|
84
|
+
* Computed as: sha256hex(Buffer.from(message).toString("hex"))
|
|
85
|
+
*/
|
|
86
|
+
messageHash: string;
|
|
87
|
+
/** ISO-8601 timestamp of the signing event. */
|
|
88
|
+
timestamp: string;
|
|
89
|
+
/** Byte length of the signature. */
|
|
90
|
+
signatureLength: number;
|
|
91
|
+
/** Whether the signature verified correctly at audit time. */
|
|
92
|
+
verifiedAtAudit: boolean;
|
|
93
|
+
}
|
|
94
|
+
export declare class MlDsaSigner {
|
|
95
|
+
#private;
|
|
96
|
+
/**
|
|
97
|
+
* Generate a new ML-DSA keypair.
|
|
98
|
+
*
|
|
99
|
+
* The public key is shared with anyone who needs to verify your signatures.
|
|
100
|
+
* The secret key must be stored securely — ideally in an HSM or
|
|
101
|
+
* hardware-backed key store.
|
|
102
|
+
*/
|
|
103
|
+
generateKeyPair(params: MlDsaParams): DsaKeyPair;
|
|
104
|
+
/**
|
|
105
|
+
* Sign a message.
|
|
106
|
+
*
|
|
107
|
+
* @param message - Raw bytes of the message to sign.
|
|
108
|
+
* @param secretKey - The signer's ML-DSA secret key.
|
|
109
|
+
* @param params - Parameter set that was used to generate the keypair.
|
|
110
|
+
* @returns Signature bytes and an audit commitment hash.
|
|
111
|
+
*/
|
|
112
|
+
sign(message: Uint8Array, secretKey: Uint8Array, params: MlDsaParams): DsaSignatureResult;
|
|
113
|
+
/**
|
|
114
|
+
* Verify a signature.
|
|
115
|
+
*
|
|
116
|
+
* @param message - The original message bytes.
|
|
117
|
+
* @param signature - The signature to verify.
|
|
118
|
+
* @param publicKey - The signer's ML-DSA public key.
|
|
119
|
+
* @param params - Parameter set used to generate the keypair.
|
|
120
|
+
* @returns `true` if the signature is valid, `false` otherwise.
|
|
121
|
+
*
|
|
122
|
+
* This method never throws on invalid signature bytes — it returns `false`.
|
|
123
|
+
* This ensures it cannot be used as an oracle for timing attacks.
|
|
124
|
+
*/
|
|
125
|
+
verify(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array, params: MlDsaParams): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Produce an on-chain-ready audit record for a signing event.
|
|
128
|
+
*
|
|
129
|
+
* The returned object contains only hashes — no secret material — and is
|
|
130
|
+
* safe to store in a ledger or audit log.
|
|
131
|
+
*/
|
|
132
|
+
auditRecord(message: Uint8Array, result: DsaSignatureResult, publicKey: Uint8Array, params: MlDsaParams): DsaAuditRecord;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=dsa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsa.d.ts","sourceRoot":"","sources":["../../src/mpc/dsa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAMlE;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAC/B,WAAW,EACX;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAK5D,CAAC;AAMF,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;IACtB,0DAA0D;IAC1D,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,SAAS,EAAE,UAAU,CAAC;IACtB;;;;;;;;OAQG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;CAC1B;AAMD,qBAAa,WAAW;;IACtB;;;;;;OAMG;IACH,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU;IAMhD;;;;;;;OAOG;IACH,IAAI,CACF,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE,WAAW,GAClB,kBAAkB;IAQrB;;;;;;;;;;;OAWG;IACH,MAAM,CACJ,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE,WAAW,GAClB,OAAO;IAUV;;;;;OAKG;IACH,WAAW,CACT,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,kBAAkB,EAC1B,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE,WAAW,GAClB,cAAc;CA2BlB"}
|
package/dist/mpc/dsa.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ML-DSA (Dilithium) — Digital Signature Algorithm
|
|
3
|
+
*
|
|
4
|
+
* Classical signature schemes (ECDSA, RSA) rely on the hardness of the
|
|
5
|
+
* discrete logarithm and integer factorisation problems. Shor's algorithm,
|
|
6
|
+
* running on a sufficiently large cryptographically-relevant quantum computer
|
|
7
|
+
* (CRQC), solves both in polynomial time — meaning historical signatures could
|
|
8
|
+
* be forged retroactively ("harvest-now, forge-later" threat to long-lived
|
|
9
|
+
* documents and trade rails).
|
|
10
|
+
*
|
|
11
|
+
* ML-DSA is the NIST-standardised Digital Signature Algorithm designed to
|
|
12
|
+
* replace ECDSA. It is based on the Module-Lattice problem (MLWE / MSIS),
|
|
13
|
+
* which has no known quantum speedup beyond Grover's algorithm.
|
|
14
|
+
*
|
|
15
|
+
* Standard: NIST FIPS 204 (finalised August 2024)
|
|
16
|
+
* Reference: https://csrc.nist.gov/pubs/fips/204/final
|
|
17
|
+
*
|
|
18
|
+
* Noble API note (differs from ML-KEM):
|
|
19
|
+
* sign(message, secretKey) — message is the FIRST argument
|
|
20
|
+
* verify(signature, message, publicKey)
|
|
21
|
+
*
|
|
22
|
+
* This module wraps @noble/post-quantum, a pure-TypeScript implementation with
|
|
23
|
+
* zero native-code dependencies, which makes it platform-portable and easy to
|
|
24
|
+
* audit.
|
|
25
|
+
*/
|
|
26
|
+
import { ml_dsa44, ml_dsa65, ml_dsa87 } from "@noble/post-quantum/ml-dsa.js";
|
|
27
|
+
import { sha256hex } from "./crypto.js";
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Wire-format byte lengths (empirically verified against @noble/post-quantum)
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Public key, secret key, and signature lengths (in bytes) for each
|
|
33
|
+
* parameter set. Useful for validation and test assertions.
|
|
34
|
+
*
|
|
35
|
+
* Note: Noble's secretKey representation includes expanded key material;
|
|
36
|
+
* hence sk lengths are larger than the FIPS 204 §7 algebraic key sizes.
|
|
37
|
+
*/
|
|
38
|
+
export const ML_DSA_SIZES = {
|
|
39
|
+
"ml-dsa-44": { publicKey: 1312, secretKey: 2560, signature: 2420 },
|
|
40
|
+
"ml-dsa-65": { publicKey: 1952, secretKey: 4032, signature: 3309 },
|
|
41
|
+
"ml-dsa-87": { publicKey: 2592, secretKey: 4896, signature: 4627 },
|
|
42
|
+
};
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// MlDsaSigner class
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
export class MlDsaSigner {
|
|
47
|
+
/**
|
|
48
|
+
* Generate a new ML-DSA keypair.
|
|
49
|
+
*
|
|
50
|
+
* The public key is shared with anyone who needs to verify your signatures.
|
|
51
|
+
* The secret key must be stored securely — ideally in an HSM or
|
|
52
|
+
* hardware-backed key store.
|
|
53
|
+
*/
|
|
54
|
+
generateKeyPair(params) {
|
|
55
|
+
const dsa = this.#suite(params);
|
|
56
|
+
const { publicKey, secretKey } = dsa.keygen();
|
|
57
|
+
return { publicKey, secretKey, params };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Sign a message.
|
|
61
|
+
*
|
|
62
|
+
* @param message - Raw bytes of the message to sign.
|
|
63
|
+
* @param secretKey - The signer's ML-DSA secret key.
|
|
64
|
+
* @param params - Parameter set that was used to generate the keypair.
|
|
65
|
+
* @returns Signature bytes and an audit commitment hash.
|
|
66
|
+
*/
|
|
67
|
+
sign(message, secretKey, params) {
|
|
68
|
+
const dsa = this.#suite(params);
|
|
69
|
+
// Noble ML-DSA API: sign(message, secretKey)
|
|
70
|
+
const signature = dsa.sign(message, secretKey);
|
|
71
|
+
const auditCommitment = sha256hex(Buffer.from(signature).toString("hex"));
|
|
72
|
+
return { signature, auditCommitment };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Verify a signature.
|
|
76
|
+
*
|
|
77
|
+
* @param message - The original message bytes.
|
|
78
|
+
* @param signature - The signature to verify.
|
|
79
|
+
* @param publicKey - The signer's ML-DSA public key.
|
|
80
|
+
* @param params - Parameter set used to generate the keypair.
|
|
81
|
+
* @returns `true` if the signature is valid, `false` otherwise.
|
|
82
|
+
*
|
|
83
|
+
* This method never throws on invalid signature bytes — it returns `false`.
|
|
84
|
+
* This ensures it cannot be used as an oracle for timing attacks.
|
|
85
|
+
*/
|
|
86
|
+
verify(message, signature, publicKey, params) {
|
|
87
|
+
try {
|
|
88
|
+
const dsa = this.#suite(params);
|
|
89
|
+
// Noble ML-DSA API: verify(signature, message, publicKey)
|
|
90
|
+
return dsa.verify(signature, message, publicKey);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Produce an on-chain-ready audit record for a signing event.
|
|
98
|
+
*
|
|
99
|
+
* The returned object contains only hashes — no secret material — and is
|
|
100
|
+
* safe to store in a ledger or audit log.
|
|
101
|
+
*/
|
|
102
|
+
auditRecord(message, result, publicKey, params) {
|
|
103
|
+
const verified = this.verify(message, result.signature, publicKey, params);
|
|
104
|
+
return {
|
|
105
|
+
params,
|
|
106
|
+
publicKeyHash: sha256hex(Buffer.from(publicKey).toString("hex")),
|
|
107
|
+
signatureHash: result.auditCommitment,
|
|
108
|
+
messageHash: sha256hex(Buffer.from(message).toString("hex")),
|
|
109
|
+
timestamp: new Date().toISOString(),
|
|
110
|
+
signatureLength: result.signature.length,
|
|
111
|
+
verifiedAtAudit: verified,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Private helpers
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
#suite(params) {
|
|
118
|
+
switch (params) {
|
|
119
|
+
case "ml-dsa-44":
|
|
120
|
+
return ml_dsa44;
|
|
121
|
+
case "ml-dsa-65":
|
|
122
|
+
return ml_dsa65;
|
|
123
|
+
case "ml-dsa-87":
|
|
124
|
+
return ml_dsa87;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|