@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,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finite field arithmetic for Shamir Secret Sharing.
|
|
3
|
+
*
|
|
4
|
+
* Two field sizes are supported:
|
|
5
|
+
* - **Demo mode**: 2^31 - 1 (Mersenne prime, fast, fits in JS Number)
|
|
6
|
+
* - **Production mode** (default): 2^256 - 2^32 - 977 (secp256k1 order, cryptographically secure)
|
|
7
|
+
*
|
|
8
|
+
* WARNING: Demo mode is NOT secure for production use.
|
|
9
|
+
* The small field allows brute-force enumeration in seconds.
|
|
10
|
+
* Demo mode is only allowed when NODE_ENV is "development" or "test".
|
|
11
|
+
*
|
|
12
|
+
* @see skills/mpc-secret-sharing.md for security guidance
|
|
13
|
+
* @see docs/adr/ADR-0004-field-arithmetic-for-mpc.md for design rationale
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Demo field: Mersenne prime 2^31 - 1.
|
|
17
|
+
* Fast arithmetic, fits in JS safe integer range.
|
|
18
|
+
* NOT SECURE for production - enumerable in ~seconds on modern hardware.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEMO_PRIME = 2147483647n;
|
|
21
|
+
/**
|
|
22
|
+
* Production field: secp256k1 curve order.
|
|
23
|
+
* 256-bit prime providing 128+ bits of security against enumeration.
|
|
24
|
+
* Used by Bitcoin, Ethereum, and most production secret sharing implementations.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PRODUCTION_PRIME = 115792089237316195423570985008687907852837564279074904382605163141518161494337n;
|
|
27
|
+
/**
|
|
28
|
+
* Alternative production field: 2^255 - 19 (Curve25519 prime).
|
|
29
|
+
* Slightly faster modular reduction due to special form.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CURVE25519_PRIME = 57896044618658097711785492504343953926634992332820282019728792003956564819949n;
|
|
32
|
+
export type FieldMode = "demo" | "production";
|
|
33
|
+
export interface FieldConfig {
|
|
34
|
+
mode: FieldMode;
|
|
35
|
+
prime: bigint;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get field configuration from environment or explicit parameter.
|
|
39
|
+
*
|
|
40
|
+
* Environment variable: MPC_FIELD_MODE ("demo" | "production")
|
|
41
|
+
* Default: "production" (secure by default)
|
|
42
|
+
*
|
|
43
|
+
* Demo mode requires NODE_ENV to be "development" or "test".
|
|
44
|
+
* This prevents accidental deployment with insecure field size.
|
|
45
|
+
*
|
|
46
|
+
* @param mode - Explicit mode override (optional)
|
|
47
|
+
* @returns Field configuration with prime and mode
|
|
48
|
+
* @throws Error if demo mode is requested outside dev/test environment
|
|
49
|
+
*/
|
|
50
|
+
export declare function getFieldConfig(mode?: FieldMode): FieldConfig;
|
|
51
|
+
/**
|
|
52
|
+
* Field arithmetic operations for a given prime.
|
|
53
|
+
*/
|
|
54
|
+
export declare class FieldArithmetic {
|
|
55
|
+
readonly prime: bigint;
|
|
56
|
+
readonly mode: FieldMode;
|
|
57
|
+
constructor(config?: FieldConfig);
|
|
58
|
+
/**
|
|
59
|
+
* Modular reduction: ensures result is in [0, prime).
|
|
60
|
+
*/
|
|
61
|
+
mod(a: bigint): bigint;
|
|
62
|
+
/**
|
|
63
|
+
* Modular addition: (a + b) mod prime.
|
|
64
|
+
*/
|
|
65
|
+
add(a: bigint, b: bigint): bigint;
|
|
66
|
+
/**
|
|
67
|
+
* Modular subtraction: (a - b) mod prime.
|
|
68
|
+
*/
|
|
69
|
+
sub(a: bigint, b: bigint): bigint;
|
|
70
|
+
/**
|
|
71
|
+
* Modular multiplication: (a * b) mod prime.
|
|
72
|
+
*/
|
|
73
|
+
mul(a: bigint, b: bigint): bigint;
|
|
74
|
+
/**
|
|
75
|
+
* Modular exponentiation: base^exp mod prime.
|
|
76
|
+
* Uses square-and-multiply for O(log exp) complexity.
|
|
77
|
+
*/
|
|
78
|
+
pow(base: bigint, exp: bigint): bigint;
|
|
79
|
+
/**
|
|
80
|
+
* Modular multiplicative inverse: a^(-1) mod prime.
|
|
81
|
+
* Uses Fermat's little theorem: a^(-1) = a^(p-2) mod p.
|
|
82
|
+
*/
|
|
83
|
+
inverse(a: bigint): bigint;
|
|
84
|
+
/**
|
|
85
|
+
* Modular division: a / b mod prime = a * b^(-1) mod prime.
|
|
86
|
+
*/
|
|
87
|
+
div(a: bigint, b: bigint): bigint;
|
|
88
|
+
/**
|
|
89
|
+
* Generate a random field element in [1, prime).
|
|
90
|
+
* Uses cryptographically secure random bytes.
|
|
91
|
+
*/
|
|
92
|
+
random(): bigint;
|
|
93
|
+
/**
|
|
94
|
+
* Convert a number to a field element.
|
|
95
|
+
* Throws if the number is outside the valid range.
|
|
96
|
+
*/
|
|
97
|
+
fromNumber(n: number): bigint;
|
|
98
|
+
/**
|
|
99
|
+
* Convert a field element to a number.
|
|
100
|
+
* Throws if the value exceeds JS safe integer range.
|
|
101
|
+
*/
|
|
102
|
+
toNumber(value: bigint): number;
|
|
103
|
+
/**
|
|
104
|
+
* Check if this is a production-grade field.
|
|
105
|
+
*/
|
|
106
|
+
isProductionSecure(): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Get the security level in bits.
|
|
109
|
+
* Demo mode: ~31 bits (insecure)
|
|
110
|
+
* Production mode: ~128 bits (secure)
|
|
111
|
+
*/
|
|
112
|
+
getSecurityBits(): number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Production field singleton for common use cases.
|
|
116
|
+
* This is the recommended default for all production code.
|
|
117
|
+
*/
|
|
118
|
+
export declare const productionField: FieldArithmetic;
|
|
119
|
+
/**
|
|
120
|
+
* Demo field singleton - INTERNAL USE ONLY.
|
|
121
|
+
* Not exported to prevent accidental use in production code.
|
|
122
|
+
* Use `new FieldArithmetic({ mode: "demo", prime: DEMO_PRIME })` explicitly
|
|
123
|
+
* in development/test contexts only.
|
|
124
|
+
*/
|
|
125
|
+
declare const _demoField: FieldArithmetic;
|
|
126
|
+
export { _demoField as demoField };
|
|
127
|
+
//# sourceMappingURL=field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/mpc/field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;GAIG;AACH,eAAO,MAAM,UAAU,cAAiB,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,kFACwC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,iFACwC,CAAC;AAEtE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CA+B5D;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;gBAEb,MAAM,GAAE,WAA8B;IAKlD;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;IAItB;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAIjC;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAIjC;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAIjC;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAetC;;;OAGG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;IAO1B;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAIjC;;;OAGG;IACH,MAAM,IAAI,MAAM;IAchB;;;OAGG;IACH,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;IAa7B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAU/B;;OAEG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;;;OAIG;IACH,eAAe,IAAI,MAAM;CAG1B;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,iBAG1B,CAAC;AAEH;;;;;GAKG;AACH,QAAA,MAAM,UAAU,iBAGd,CAAC;AAGH,OAAO,EAAE,UAAU,IAAI,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finite field arithmetic for Shamir Secret Sharing.
|
|
3
|
+
*
|
|
4
|
+
* Two field sizes are supported:
|
|
5
|
+
* - **Demo mode**: 2^31 - 1 (Mersenne prime, fast, fits in JS Number)
|
|
6
|
+
* - **Production mode** (default): 2^256 - 2^32 - 977 (secp256k1 order, cryptographically secure)
|
|
7
|
+
*
|
|
8
|
+
* WARNING: Demo mode is NOT secure for production use.
|
|
9
|
+
* The small field allows brute-force enumeration in seconds.
|
|
10
|
+
* Demo mode is only allowed when NODE_ENV is "development" or "test".
|
|
11
|
+
*
|
|
12
|
+
* @see skills/mpc-secret-sharing.md for security guidance
|
|
13
|
+
* @see docs/adr/ADR-0004-field-arithmetic-for-mpc.md for design rationale
|
|
14
|
+
*/
|
|
15
|
+
import { randomBytes } from "node:crypto";
|
|
16
|
+
/**
|
|
17
|
+
* Demo field: Mersenne prime 2^31 - 1.
|
|
18
|
+
* Fast arithmetic, fits in JS safe integer range.
|
|
19
|
+
* NOT SECURE for production - enumerable in ~seconds on modern hardware.
|
|
20
|
+
*/
|
|
21
|
+
export const DEMO_PRIME = 2147483647n;
|
|
22
|
+
/**
|
|
23
|
+
* Production field: secp256k1 curve order.
|
|
24
|
+
* 256-bit prime providing 128+ bits of security against enumeration.
|
|
25
|
+
* Used by Bitcoin, Ethereum, and most production secret sharing implementations.
|
|
26
|
+
*/
|
|
27
|
+
export const PRODUCTION_PRIME = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
28
|
+
/**
|
|
29
|
+
* Alternative production field: 2^255 - 19 (Curve25519 prime).
|
|
30
|
+
* Slightly faster modular reduction due to special form.
|
|
31
|
+
*/
|
|
32
|
+
export const CURVE25519_PRIME = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn;
|
|
33
|
+
/**
|
|
34
|
+
* Get field configuration from environment or explicit parameter.
|
|
35
|
+
*
|
|
36
|
+
* Environment variable: MPC_FIELD_MODE ("demo" | "production")
|
|
37
|
+
* Default: "production" (secure by default)
|
|
38
|
+
*
|
|
39
|
+
* Demo mode requires NODE_ENV to be "development" or "test".
|
|
40
|
+
* This prevents accidental deployment with insecure field size.
|
|
41
|
+
*
|
|
42
|
+
* @param mode - Explicit mode override (optional)
|
|
43
|
+
* @returns Field configuration with prime and mode
|
|
44
|
+
* @throws Error if demo mode is requested outside dev/test environment
|
|
45
|
+
*/
|
|
46
|
+
export function getFieldConfig(mode) {
|
|
47
|
+
const envMode = process.env.MPC_FIELD_MODE;
|
|
48
|
+
const rawMode = mode ?? envMode ?? "production";
|
|
49
|
+
// Validate the mode is one of the allowed values
|
|
50
|
+
if (rawMode !== "demo" && rawMode !== "production") {
|
|
51
|
+
throw new Error(`Invalid MPC_FIELD_MODE: "${rawMode}". Use "demo" or "production".`);
|
|
52
|
+
}
|
|
53
|
+
const effectiveMode = rawMode;
|
|
54
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
55
|
+
const isDevelopment = nodeEnv === "development" || nodeEnv === "test";
|
|
56
|
+
// SECURITY: Demo mode is ONLY allowed when NODE_ENV is explicitly
|
|
57
|
+
// set to "development" or "test". No bypass or override is permitted.
|
|
58
|
+
// This prevents accidental deployment with insecure field size.
|
|
59
|
+
if (effectiveMode === "demo" && !isDevelopment) {
|
|
60
|
+
throw new Error("SECURITY ERROR: Demo field mode is only allowed when NODE_ENV is " +
|
|
61
|
+
"'development' or 'test'. Set NODE_ENV appropriately for local " +
|
|
62
|
+
"development, or set MPC_FIELD_MODE=production for cryptographic security.");
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
mode: effectiveMode,
|
|
66
|
+
prime: effectiveMode === "production" ? PRODUCTION_PRIME : DEMO_PRIME,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Field arithmetic operations for a given prime.
|
|
71
|
+
*/
|
|
72
|
+
export class FieldArithmetic {
|
|
73
|
+
prime;
|
|
74
|
+
mode;
|
|
75
|
+
constructor(config = getFieldConfig()) {
|
|
76
|
+
this.prime = config.prime;
|
|
77
|
+
this.mode = config.mode;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Modular reduction: ensures result is in [0, prime).
|
|
81
|
+
*/
|
|
82
|
+
mod(a) {
|
|
83
|
+
return ((a % this.prime) + this.prime) % this.prime;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Modular addition: (a + b) mod prime.
|
|
87
|
+
*/
|
|
88
|
+
add(a, b) {
|
|
89
|
+
return this.mod(a + b);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Modular subtraction: (a - b) mod prime.
|
|
93
|
+
*/
|
|
94
|
+
sub(a, b) {
|
|
95
|
+
return this.mod(a - b);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Modular multiplication: (a * b) mod prime.
|
|
99
|
+
*/
|
|
100
|
+
mul(a, b) {
|
|
101
|
+
return this.mod(a * b);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Modular exponentiation: base^exp mod prime.
|
|
105
|
+
* Uses square-and-multiply for O(log exp) complexity.
|
|
106
|
+
*/
|
|
107
|
+
pow(base, exp) {
|
|
108
|
+
let result = 1n;
|
|
109
|
+
base = this.mod(base);
|
|
110
|
+
while (exp > 0n) {
|
|
111
|
+
if (exp & 1n) {
|
|
112
|
+
result = this.mod(result * base);
|
|
113
|
+
}
|
|
114
|
+
exp >>= 1n;
|
|
115
|
+
base = this.mod(base * base);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Modular multiplicative inverse: a^(-1) mod prime.
|
|
121
|
+
* Uses Fermat's little theorem: a^(-1) = a^(p-2) mod p.
|
|
122
|
+
*/
|
|
123
|
+
inverse(a) {
|
|
124
|
+
if (this.mod(a) === 0n) {
|
|
125
|
+
throw new Error("Cannot compute inverse of zero");
|
|
126
|
+
}
|
|
127
|
+
return this.pow(a, this.prime - 2n);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Modular division: a / b mod prime = a * b^(-1) mod prime.
|
|
131
|
+
*/
|
|
132
|
+
div(a, b) {
|
|
133
|
+
return this.mul(a, this.inverse(b));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Generate a random field element in [1, prime).
|
|
137
|
+
* Uses cryptographically secure random bytes.
|
|
138
|
+
*/
|
|
139
|
+
random() {
|
|
140
|
+
// Generate enough bytes to cover the prime
|
|
141
|
+
const byteLength = Math.ceil(this.prime.toString(2).length / 8);
|
|
142
|
+
let value;
|
|
143
|
+
// Rejection sampling to ensure uniform distribution
|
|
144
|
+
do {
|
|
145
|
+
const bytes = randomBytes(byteLength);
|
|
146
|
+
value = BigInt("0x" + bytes.toString("hex"));
|
|
147
|
+
} while (value >= this.prime || value === 0n);
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Convert a number to a field element.
|
|
152
|
+
* Throws if the number is outside the valid range.
|
|
153
|
+
*/
|
|
154
|
+
fromNumber(n) {
|
|
155
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
156
|
+
throw new Error("Field element must be a non-negative integer");
|
|
157
|
+
}
|
|
158
|
+
const value = BigInt(n);
|
|
159
|
+
if (value >= this.prime) {
|
|
160
|
+
throw new Error(`Value ${n} exceeds field prime ${this.prime}`);
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Convert a field element to a number.
|
|
166
|
+
* Throws if the value exceeds JS safe integer range.
|
|
167
|
+
*/
|
|
168
|
+
toNumber(value) {
|
|
169
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
170
|
+
throw new Error(`Field element ${value} exceeds JS safe integer range. ` +
|
|
171
|
+
`Use bigint operations directly for production field.`);
|
|
172
|
+
}
|
|
173
|
+
return Number(value);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Check if this is a production-grade field.
|
|
177
|
+
*/
|
|
178
|
+
isProductionSecure() {
|
|
179
|
+
return this.mode === "production";
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get the security level in bits.
|
|
183
|
+
* Demo mode: ~31 bits (insecure)
|
|
184
|
+
* Production mode: ~128 bits (secure)
|
|
185
|
+
*/
|
|
186
|
+
getSecurityBits() {
|
|
187
|
+
return this.mode === "production" ? 128 : 31;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Production field singleton for common use cases.
|
|
192
|
+
* This is the recommended default for all production code.
|
|
193
|
+
*/
|
|
194
|
+
export const productionField = new FieldArithmetic({
|
|
195
|
+
mode: "production",
|
|
196
|
+
prime: PRODUCTION_PRIME,
|
|
197
|
+
});
|
|
198
|
+
/**
|
|
199
|
+
* Demo field singleton - INTERNAL USE ONLY.
|
|
200
|
+
* Not exported to prevent accidental use in production code.
|
|
201
|
+
* Use `new FieldArithmetic({ mode: "demo", prime: DEMO_PRIME })` explicitly
|
|
202
|
+
* in development/test contexts only.
|
|
203
|
+
*/
|
|
204
|
+
const _demoField = new FieldArithmetic({
|
|
205
|
+
mode: "demo",
|
|
206
|
+
prime: DEMO_PRIME,
|
|
207
|
+
});
|
|
208
|
+
// Export for internal test usage only (not re-exported from index.ts)
|
|
209
|
+
export { _demoField as demoField };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid KEM — X25519 + ML-KEM-768
|
|
3
|
+
*
|
|
4
|
+
* Why hybrid instead of ML-KEM alone?
|
|
5
|
+
*
|
|
6
|
+
* We are in a cryptographic transition period. CRQCs capable of running
|
|
7
|
+
* Shor's algorithm at scale do not yet exist, but quantum-safe algorithms are
|
|
8
|
+
* newer and have had less time for public cryptanalysis than well-established
|
|
9
|
+
* classical schemes.
|
|
10
|
+
*
|
|
11
|
+
* A hybrid KEM addresses both risks simultaneously:
|
|
12
|
+
* 1. Classical channel (X25519) — secure against all known classical attacks
|
|
13
|
+
* 2. Post-quantum channel (ML-KEM-768) — secure against quantum adversaries
|
|
14
|
+
*
|
|
15
|
+
* Both shared secrets are fed into HKDF together, so breaking the combined
|
|
16
|
+
* key requires breaking *both* channels. This matches how Chrome, Firefox, and
|
|
17
|
+
* Cloudflare deployed post-quantum TLS during the 2023–2024 transition
|
|
18
|
+
* (Google's X25519Kyber768 experiment: https://blog.chromium.org/2023/08/protecting-chrome-traffic-with-hybrid.html).
|
|
19
|
+
*
|
|
20
|
+
* The construction used here follows the approach described in:
|
|
21
|
+
* IETF draft-ietf-tls-hybrid-design — https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/
|
|
22
|
+
*
|
|
23
|
+
* Standard for the PQ half: NIST FIPS 203 — https://csrc.nist.gov/pubs/fips/203/final
|
|
24
|
+
*/
|
|
25
|
+
import type { KeyObject } from "node:crypto";
|
|
26
|
+
import type { KyberKeyPair } from "./kyber.js";
|
|
27
|
+
export interface HybridKeyPairs {
|
|
28
|
+
/** Ephemeral X25519 key pair (classical channel). */
|
|
29
|
+
x25519: {
|
|
30
|
+
publicKey: KeyObject;
|
|
31
|
+
privateKey: KeyObject;
|
|
32
|
+
};
|
|
33
|
+
/** ML-KEM-768 key pair (post-quantum channel). */
|
|
34
|
+
kyber: KyberKeyPair;
|
|
35
|
+
}
|
|
36
|
+
export interface HybridEncapsulation {
|
|
37
|
+
/**
|
|
38
|
+
* X25519 ephemeral public key in DER format.
|
|
39
|
+
* The recipient uses this together with their X25519 private key to
|
|
40
|
+
* reproduce the classical shared secret.
|
|
41
|
+
*/
|
|
42
|
+
x25519EphemeralPublicKeyDer: Buffer;
|
|
43
|
+
/** ML-KEM-768 ciphertext (post-quantum channel). */
|
|
44
|
+
kyberCiphertext: Uint8Array;
|
|
45
|
+
/**
|
|
46
|
+
* The combined symmetric key derived from both channels.
|
|
47
|
+
* 32 bytes — ready for use as an AES-256-GCM key.
|
|
48
|
+
* Do not transmit; derive it independently on each side.
|
|
49
|
+
*/
|
|
50
|
+
combinedKey: Buffer;
|
|
51
|
+
/**
|
|
52
|
+
* SHA-256 of the concatenated hex encodings for auditing.
|
|
53
|
+
*
|
|
54
|
+
* Computed as: sha256hex(ephemeralPubDer.toString("hex") + kyberCiphertext.toString("hex"))
|
|
55
|
+
*
|
|
56
|
+
* This hashes the hex-encoded string concatenation, not the raw bytes.
|
|
57
|
+
* Suitable for on-chain commitments or audit logs.
|
|
58
|
+
*/
|
|
59
|
+
auditCommitment: string;
|
|
60
|
+
}
|
|
61
|
+
export interface HybridDecapsulation {
|
|
62
|
+
combinedKey: Buffer;
|
|
63
|
+
}
|
|
64
|
+
export declare class HybridKem {
|
|
65
|
+
#private;
|
|
66
|
+
/**
|
|
67
|
+
* Generate a fresh set of long-term recipient key pairs (one per channel).
|
|
68
|
+
*
|
|
69
|
+
* In production the X25519 and ML-KEM keys would live in separate HSM slots
|
|
70
|
+
* and be rotated on independent schedules. Here we generate both for
|
|
71
|
+
* demonstration purposes.
|
|
72
|
+
*/
|
|
73
|
+
generateKeyPairs(): HybridKeyPairs;
|
|
74
|
+
/**
|
|
75
|
+
* Sender-side encapsulation.
|
|
76
|
+
*
|
|
77
|
+
* 1. Generate an ephemeral X25519 keypair and perform DH with the
|
|
78
|
+
* recipient's X25519 public key to get the classical shared secret.
|
|
79
|
+
* 2. Encapsulate a fresh shared secret under the recipient's ML-KEM-768
|
|
80
|
+
* public key.
|
|
81
|
+
* 3. Feed both shared secrets into HKDF together to produce one combined
|
|
82
|
+
* symmetric key.
|
|
83
|
+
*
|
|
84
|
+
* Only `x25519EphemeralPublicKeyDer` and `kyberCiphertext` are transmitted.
|
|
85
|
+
* The `combinedKey` stays local and is used for encryption.
|
|
86
|
+
*/
|
|
87
|
+
encapsulate(recipientX25519PublicKey: KeyObject, recipientKyberPublicKey: Uint8Array): HybridEncapsulation;
|
|
88
|
+
/**
|
|
89
|
+
* Receiver-side decapsulation.
|
|
90
|
+
*
|
|
91
|
+
* Reproduces the same `combinedKey` as the sender using the two received
|
|
92
|
+
* ciphertexts and the recipient's secret keys.
|
|
93
|
+
*/
|
|
94
|
+
decapsulate(recipientX25519PrivateKey: KeyObject, recipientKyberSecretKey: Uint8Array, x25519EphemeralPublicKeyDer: Buffer, kyberCiphertext: Uint8Array): HybridDecapsulation;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=hybrid-kem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hybrid-kem.d.ts","sourceRoot":"","sources":["../../src/mpc/hybrid-kem.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AASH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,MAAM,EAAE;QACN,SAAS,EAAE,SAAS,CAAC;QACrB,UAAU,EAAE,SAAS,CAAC;KACvB,CAAC;IACF,kDAAkD;IAClD,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,2BAA2B,EAAE,MAAM,CAAC;IACpC,oDAAoD;IACpD,eAAe,EAAE,UAAU,CAAC;IAC5B;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,qBAAa,SAAS;;IACpB;;;;;;OAMG;IACH,gBAAgB,IAAI,cAAc;IASlC;;;;;;;;;;;;OAYG;IACH,WAAW,CACT,wBAAwB,EAAE,SAAS,EACnC,uBAAuB,EAAE,UAAU,GAClC,mBAAmB;IAuCtB;;;;;OAKG;IACH,WAAW,CACT,yBAAyB,EAAE,SAAS,EACpC,uBAAuB,EAAE,UAAU,EACnC,2BAA2B,EAAE,MAAM,EACnC,eAAe,EAAE,UAAU,GAC1B,mBAAmB;CAqDvB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid KEM — X25519 + ML-KEM-768
|
|
3
|
+
*
|
|
4
|
+
* Why hybrid instead of ML-KEM alone?
|
|
5
|
+
*
|
|
6
|
+
* We are in a cryptographic transition period. CRQCs capable of running
|
|
7
|
+
* Shor's algorithm at scale do not yet exist, but quantum-safe algorithms are
|
|
8
|
+
* newer and have had less time for public cryptanalysis than well-established
|
|
9
|
+
* classical schemes.
|
|
10
|
+
*
|
|
11
|
+
* A hybrid KEM addresses both risks simultaneously:
|
|
12
|
+
* 1. Classical channel (X25519) — secure against all known classical attacks
|
|
13
|
+
* 2. Post-quantum channel (ML-KEM-768) — secure against quantum adversaries
|
|
14
|
+
*
|
|
15
|
+
* Both shared secrets are fed into HKDF together, so breaking the combined
|
|
16
|
+
* key requires breaking *both* channels. This matches how Chrome, Firefox, and
|
|
17
|
+
* Cloudflare deployed post-quantum TLS during the 2023–2024 transition
|
|
18
|
+
* (Google's X25519Kyber768 experiment: https://blog.chromium.org/2023/08/protecting-chrome-traffic-with-hybrid.html).
|
|
19
|
+
*
|
|
20
|
+
* The construction used here follows the approach described in:
|
|
21
|
+
* IETF draft-ietf-tls-hybrid-design — https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/
|
|
22
|
+
*
|
|
23
|
+
* Standard for the PQ half: NIST FIPS 203 — https://csrc.nist.gov/pubs/fips/203/final
|
|
24
|
+
*/
|
|
25
|
+
var _a;
|
|
26
|
+
import { createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, createHash, } from "node:crypto";
|
|
27
|
+
import { ml_kem768 } from "@noble/post-quantum/ml-kem.js";
|
|
28
|
+
import { sha256hex } from "./crypto.js";
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// HybridKem class
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
export class HybridKem {
|
|
33
|
+
/**
|
|
34
|
+
* Generate a fresh set of long-term recipient key pairs (one per channel).
|
|
35
|
+
*
|
|
36
|
+
* In production the X25519 and ML-KEM keys would live in separate HSM slots
|
|
37
|
+
* and be rotated on independent schedules. Here we generate both for
|
|
38
|
+
* demonstration purposes.
|
|
39
|
+
*/
|
|
40
|
+
generateKeyPairs() {
|
|
41
|
+
const { publicKey, privateKey } = generateKeyPairSync("x25519");
|
|
42
|
+
const { publicKey: kyberPub, secretKey: kyberSec } = ml_kem768.keygen();
|
|
43
|
+
return {
|
|
44
|
+
x25519: { publicKey, privateKey },
|
|
45
|
+
kyber: { publicKey: kyberPub, secretKey: kyberSec, params: "ml-kem-768" },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Sender-side encapsulation.
|
|
50
|
+
*
|
|
51
|
+
* 1. Generate an ephemeral X25519 keypair and perform DH with the
|
|
52
|
+
* recipient's X25519 public key to get the classical shared secret.
|
|
53
|
+
* 2. Encapsulate a fresh shared secret under the recipient's ML-KEM-768
|
|
54
|
+
* public key.
|
|
55
|
+
* 3. Feed both shared secrets into HKDF together to produce one combined
|
|
56
|
+
* symmetric key.
|
|
57
|
+
*
|
|
58
|
+
* Only `x25519EphemeralPublicKeyDer` and `kyberCiphertext` are transmitted.
|
|
59
|
+
* The `combinedKey` stays local and is used for encryption.
|
|
60
|
+
*/
|
|
61
|
+
encapsulate(recipientX25519PublicKey, recipientKyberPublicKey) {
|
|
62
|
+
// --- Classical channel ---
|
|
63
|
+
const { privateKey: ephemeralPriv, publicKey: ephemeralPub } = generateKeyPairSync("x25519");
|
|
64
|
+
const x25519SharedSecret = diffieHellman({
|
|
65
|
+
privateKey: ephemeralPriv,
|
|
66
|
+
publicKey: recipientX25519PublicKey,
|
|
67
|
+
});
|
|
68
|
+
// --- Post-quantum channel ---
|
|
69
|
+
const { cipherText: kyberCiphertext, sharedSecret: kyberSharedSecret } = ml_kem768.encapsulate(recipientKyberPublicKey);
|
|
70
|
+
// --- Combine both secrets via HKDF ---
|
|
71
|
+
// Concatenating before HKDF means neither secret dominates — both must be
|
|
72
|
+
// known to derive the output. The domain-separator label prevents the
|
|
73
|
+
// same key material being reused in a different context.
|
|
74
|
+
const combinedKey = this.#combineSecrets(x25519SharedSecret, kyberSharedSecret);
|
|
75
|
+
const ephemeralPubDer = Buffer.from(ephemeralPub.export({ type: "spki", format: "der" }));
|
|
76
|
+
const auditCommitment = sha256hex(ephemeralPubDer.toString("hex") +
|
|
77
|
+
Buffer.from(kyberCiphertext).toString("hex"));
|
|
78
|
+
return {
|
|
79
|
+
x25519EphemeralPublicKeyDer: ephemeralPubDer,
|
|
80
|
+
kyberCiphertext,
|
|
81
|
+
combinedKey,
|
|
82
|
+
auditCommitment,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Receiver-side decapsulation.
|
|
87
|
+
*
|
|
88
|
+
* Reproduces the same `combinedKey` as the sender using the two received
|
|
89
|
+
* ciphertexts and the recipient's secret keys.
|
|
90
|
+
*/
|
|
91
|
+
decapsulate(recipientX25519PrivateKey, recipientKyberSecretKey, x25519EphemeralPublicKeyDer, kyberCiphertext) {
|
|
92
|
+
// --- Classical channel ---
|
|
93
|
+
// Re-import the sender's ephemeral public key from the wire format
|
|
94
|
+
const ephemeralPub = createPublicKeyFromDer(x25519EphemeralPublicKeyDer);
|
|
95
|
+
const x25519SharedSecret = diffieHellman({
|
|
96
|
+
privateKey: recipientX25519PrivateKey,
|
|
97
|
+
publicKey: ephemeralPub,
|
|
98
|
+
});
|
|
99
|
+
// --- Post-quantum channel ---
|
|
100
|
+
const kyberSharedSecret = ml_kem768.decapsulate(kyberCiphertext, recipientKyberSecretKey);
|
|
101
|
+
// --- Combine both secrets (same steps as sender) ---
|
|
102
|
+
const combinedKey = this.#combineSecrets(x25519SharedSecret, kyberSharedSecret);
|
|
103
|
+
return { combinedKey };
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Helpers
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
/**
|
|
109
|
+
* Domain-specific salt for HKDF key derivation.
|
|
110
|
+
* Per RFC 5869, salt provides additional entropy for the extraction step.
|
|
111
|
+
* Using SHA-256 of domain string ensures cryptographic binding to this context.
|
|
112
|
+
*/
|
|
113
|
+
static #HKDF_SALT = createHash("sha256")
|
|
114
|
+
.update("enterprise-blockchain:hybrid-kem-v1:salt")
|
|
115
|
+
.digest();
|
|
116
|
+
/**
|
|
117
|
+
* Derive a single 32-byte key from two independent shared secrets using
|
|
118
|
+
* HKDF-SHA256. The label "hybrid-kem-v1" acts as a domain separator.
|
|
119
|
+
*/
|
|
120
|
+
#combineSecrets(x25519Secret, kyberSecret) {
|
|
121
|
+
const ikm = Buffer.concat([x25519Secret, Buffer.from(kyberSecret)]);
|
|
122
|
+
return Buffer.from(hkdfSync("sha256", ikm, _a.#HKDF_SALT, Buffer.from("hybrid-kem-v1", "utf8"), 32));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
_a = HybridKem;
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Internal utility
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
/**
|
|
130
|
+
* Reconstruct an X25519 public key from a SPKI DER buffer received over the
|
|
131
|
+
* wire. Node.js's `createPublicKey` accepts the `der` + `type: 'spki'`
|
|
132
|
+
* combination for all standard curve families including X25519.
|
|
133
|
+
*/
|
|
134
|
+
function createPublicKeyFromDer(der) {
|
|
135
|
+
return createPublicKey({ key: der, format: "der", type: "spki" });
|
|
136
|
+
}
|