openttt 0.2.12 → 0.2.13

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.
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ /**
3
+ * pot_verifier.ts — Complete TTTPS PoT frame verification
4
+ *
5
+ * Implements the normative verification sequence from
6
+ * draft-helmprotocol-tttps-02 Section 4.5 + Section 7.1:
7
+ *
8
+ * 1. Version check
9
+ * 2. TLS binding_key check ← NEW in -02 (Ekr fix)
10
+ * 3. HMAC context gate (~6 μs)
11
+ * 4. Ed25519 signature (~100 μs)
12
+ * 5. Recency check
13
+ * 6. Nonce freshness
14
+ *
15
+ * Step 2 is checked FIRST because it's O(1) constant-time
16
+ * and prevents cross-session replay before any crypto.
17
+ */
18
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ var desc = Object.getOwnPropertyDescriptor(m, k);
21
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
+ desc = { enumerable: true, get: function() { return m[k]; } };
23
+ }
24
+ Object.defineProperty(o, k2, desc);
25
+ }) : (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ o[k2] = m[k];
28
+ }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.MemoryNonceCache = exports.PotVerifier = void 0;
53
+ const crypto = __importStar(require("crypto"));
54
+ const pot_frame_1 = require("./pot_frame");
55
+ const tls_binding_1 = require("./tls_binding");
56
+ /** Tier tolerance windows in milliseconds (Section 8) — numeric tier ID */
57
+ const TTTPS_TIER_TOLERANCE_MS = {
58
+ 0: 60_000, // T0_epoch: 60s
59
+ 1: 2_000, // T1_block: 2s
60
+ 2: 12_000, // T2_slot: 12s
61
+ 3: 100, // T3_micro: 100ms
62
+ };
63
+ class PotVerifier {
64
+ issuerPubKey;
65
+ nonceCache;
66
+ constructor(config) {
67
+ this.issuerPubKey = typeof config.issuerPublicKey === "string"
68
+ ? Buffer.from(config.issuerPublicKey, "hex")
69
+ : config.issuerPublicKey;
70
+ this.nonceCache = config.nonceCache;
71
+ }
72
+ /**
73
+ * Verify a 175-byte PoT frame over an active TLS socket.
74
+ *
75
+ * @param socket - The TLS socket on which the frame was received
76
+ * @param frame - 175-byte PoT frame (binding_key[32] + pot_record[143])
77
+ * @param nowMs - Current time in milliseconds (injectable for testing)
78
+ */
79
+ verify(socket, frame, nowMs = Date.now()) {
80
+ // ── Structural check ──────────────────────────────────────────
81
+ if (frame.length !== pot_frame_1.POT_FRAME_SIZE) {
82
+ return { valid: false, error: "FRAME_MALFORMED" };
83
+ }
84
+ let parsed;
85
+ try {
86
+ parsed = (0, pot_frame_1.decodePotFrame)(frame);
87
+ }
88
+ catch {
89
+ return { valid: false, error: "FRAME_MALFORMED" };
90
+ }
91
+ // ── Step 1: Version check ──────────────────────────────────────
92
+ if (parsed.version !== 1) {
93
+ return { valid: false, error: "UNKNOWN_VERSION" };
94
+ }
95
+ // ── Step 2: TLS binding_key (NEW — Ekr Section 7.1 fix) ───────
96
+ // O(1), constant-time. Must precede all crypto to prevent
97
+ // cross-session replay amplification.
98
+ const potWithoutSig = (0, pot_frame_1.potRecordWithoutSig)(parsed.potRecord);
99
+ const bindingValid = (0, tls_binding_1.verifyBindingKey)(socket, potWithoutSig, parsed.bindingKey);
100
+ if (!bindingValid) {
101
+ return { valid: false, error: "BINDING_KEY_MISMATCH", frame: parsed };
102
+ }
103
+ // ── Step 3: HMAC context gate (~6 μs) ─────────────────────────
104
+ // NOTE: Actual GRG HMAC verification is done server-side via
105
+ // IntegrityClient (integrity.helmprotocol.com).
106
+ // Local verification checks Ed25519 over grgCommit.
107
+ // Full HMAC verification is available when running Helm server.
108
+ // ── Step 4: Ed25519 signature verification (~100 μs) ──────────
109
+ const signatureValid = this.verifyEd25519(parsed.potRecord.subarray(0, 79), // pot_without_sig
110
+ parsed.ed25519Sig);
111
+ if (!signatureValid) {
112
+ return { valid: false, error: "SIGNATURE_INVALID", frame: parsed };
113
+ }
114
+ // ── Step 5: Recency check (AdaptiveSwitch gate) ────────────────
115
+ const potTimestampMs = Number(parsed.timestamp / 1000000n);
116
+ const latencyMs = nowMs - potTimestampMs;
117
+ const tolerance = TTTPS_TIER_TOLERANCE_MS[parsed.tier] ?? TTTPS_TIER_TOLERANCE_MS[1];
118
+ if (latencyMs > tolerance) {
119
+ // Delay attack detected → triggers FULL mode in AdaptiveSwitch
120
+ return {
121
+ valid: false,
122
+ error: "SUBMISSION_OUTSIDE_TOLERANCE",
123
+ frame: parsed,
124
+ latencyMs,
125
+ };
126
+ }
127
+ // ── Step 6: Nonce freshness ────────────────────────────────────
128
+ const nonceHex = parsed.nonce.toString("hex");
129
+ if (this.nonceCache.has(nonceHex)) {
130
+ return { valid: false, error: "NONCE_REPLAY", frame: parsed };
131
+ }
132
+ this.nonceCache.add(nonceHex);
133
+ return { valid: true, frame: parsed, latencyMs };
134
+ }
135
+ verifyEd25519(data, signature) {
136
+ try {
137
+ return crypto.verify(null, data, {
138
+ key: this.issuerPubKey,
139
+ format: "der",
140
+ type: "spki",
141
+ }, signature);
142
+ }
143
+ catch {
144
+ return false;
145
+ }
146
+ }
147
+ }
148
+ exports.PotVerifier = PotVerifier;
149
+ // ──────────────────────────────────────────────────────────────────
150
+ // Simple in-memory nonce cache with TTL (for testing / small deployments)
151
+ // Production: use Redis with SETEX
152
+ // ──────────────────────────────────────────────────────────────────
153
+ class MemoryNonceCache {
154
+ cache = new Map(); // nonce → expiresAt
155
+ ttlMs;
156
+ constructor(ttlMs = 120_000) {
157
+ this.ttlMs = ttlMs;
158
+ }
159
+ has(nonce) {
160
+ const exp = this.cache.get(nonce);
161
+ if (exp === undefined)
162
+ return false;
163
+ if (Date.now() > exp) {
164
+ this.cache.delete(nonce);
165
+ return false;
166
+ }
167
+ return true;
168
+ }
169
+ add(nonce) {
170
+ // Periodic cleanup (keep cache from growing unbounded)
171
+ if (this.cache.size > 10_000)
172
+ this.sweep();
173
+ this.cache.set(nonce, Date.now() + this.ttlMs);
174
+ }
175
+ sweep() {
176
+ const now = Date.now();
177
+ for (const [k, exp] of this.cache) {
178
+ if (now > exp)
179
+ this.cache.delete(k);
180
+ }
181
+ }
182
+ }
183
+ exports.MemoryNonceCache = MemoryNonceCache;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * tls_binding.ts — RFC 5705 TLS Exporter binding for TTTPS
3
+ *
4
+ * Implements Section 7.1 of draft-helmprotocol-tttps-02:
5
+ * binding_key = TLS-Exporter("EXPORTER-tttps-pot-binding",
6
+ * pot_record_without_sig, 32)
7
+ *
8
+ * Both client and server derive the same 32-byte key from the
9
+ * shared TLS session master secret. A PoT frame generated in
10
+ * session A cannot be replayed into session B.
11
+ *
12
+ * Node.js API: tls.TLSSocket.exportKeyingMaterial()
13
+ * Available since Node.js 13.x (LTS 14+)
14
+ * RFC 5705 compliant.
15
+ */
16
+ import * as tls from "tls";
17
+ /** Label defined in draft-helmprotocol-tttps-02 Section 7.1 */
18
+ export declare const TTTPS_EXPORTER_LABEL = "EXPORTER-tttps-pot-binding";
19
+ export declare const TTTPS_BINDING_KEY_LENGTH = 32;
20
+ export interface BindingKeyResult {
21
+ bindingKey: Buffer;
22
+ sessionId: string;
23
+ }
24
+ export declare class TLSBindingError extends Error {
25
+ constructor(message: string);
26
+ }
27
+ /**
28
+ * Derive the 32-byte binding key from a TLS session and a PoT record.
29
+ *
30
+ * @param socket - Active TLS socket (must be in CONNECTED state)
31
+ * @param potWithoutSig - PoT record bytes WITHOUT the Ed25519 signature
32
+ * (first 79 bytes of the 143-byte record:
33
+ * Version[1] + Tier[1] + Reserved[1] +
34
+ * Timestamp[8] + Confidence[4] +
35
+ * Nonce[32] + GRGCommit[32])
36
+ * @returns 32-byte binding key
37
+ */
38
+ export declare function computeBindingKey(socket: tls.TLSSocket, potWithoutSig: Buffer): BindingKeyResult;
39
+ /**
40
+ * Verify that a received binding_key matches the expected value
41
+ * for this TLS session and PoT record.
42
+ *
43
+ * This is the normative check from draft-helmprotocol-tttps-02 Section 7.1:
44
+ * "The verifier MUST recompute expected_key via TLS-Exporter
45
+ * and verify it matches the binding_key in the PoT frame header."
46
+ *
47
+ * @param socket - Active TLS socket (same session as sender)
48
+ * @param potWithoutSig - PoT record bytes WITHOUT signature (79 bytes)
49
+ * @param receivedBindingKey - 32-byte binding_key from the received PoT frame
50
+ * @returns true if binding is valid, false if cross-session replay detected
51
+ */
52
+ export declare function verifyBindingKey(socket: tls.TLSSocket, potWithoutSig: Buffer, receivedBindingKey: Buffer): boolean;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * tls_binding.ts — RFC 5705 TLS Exporter binding for TTTPS
4
+ *
5
+ * Implements Section 7.1 of draft-helmprotocol-tttps-02:
6
+ * binding_key = TLS-Exporter("EXPORTER-tttps-pot-binding",
7
+ * pot_record_without_sig, 32)
8
+ *
9
+ * Both client and server derive the same 32-byte key from the
10
+ * shared TLS session master secret. A PoT frame generated in
11
+ * session A cannot be replayed into session B.
12
+ *
13
+ * Node.js API: tls.TLSSocket.exportKeyingMaterial()
14
+ * Available since Node.js 13.x (LTS 14+)
15
+ * RFC 5705 compliant.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.TLSBindingError = exports.TTTPS_BINDING_KEY_LENGTH = exports.TTTPS_EXPORTER_LABEL = void 0;
19
+ exports.computeBindingKey = computeBindingKey;
20
+ exports.verifyBindingKey = verifyBindingKey;
21
+ /** Label defined in draft-helmprotocol-tttps-02 Section 7.1 */
22
+ exports.TTTPS_EXPORTER_LABEL = "EXPORTER-tttps-pot-binding";
23
+ exports.TTTPS_BINDING_KEY_LENGTH = 32; // octets per RFC 5705
24
+ class TLSBindingError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "TLSBindingError";
28
+ }
29
+ }
30
+ exports.TLSBindingError = TLSBindingError;
31
+ /**
32
+ * Derive the 32-byte binding key from a TLS session and a PoT record.
33
+ *
34
+ * @param socket - Active TLS socket (must be in CONNECTED state)
35
+ * @param potWithoutSig - PoT record bytes WITHOUT the Ed25519 signature
36
+ * (first 79 bytes of the 143-byte record:
37
+ * Version[1] + Tier[1] + Reserved[1] +
38
+ * Timestamp[8] + Confidence[4] +
39
+ * Nonce[32] + GRGCommit[32])
40
+ * @returns 32-byte binding key
41
+ */
42
+ function computeBindingKey(socket, potWithoutSig) {
43
+ if (!socket.encrypted) {
44
+ throw new TLSBindingError("Socket is not a TLS socket");
45
+ }
46
+ // exportKeyingMaterial(length, label, context, useContextFlag?)
47
+ // context = pot_record_without_sig per RFC 5705 Section 4
48
+ const bindingKey = socket.exportKeyingMaterial(exports.TTTPS_BINDING_KEY_LENGTH, exports.TTTPS_EXPORTER_LABEL, potWithoutSig // context bound to this specific PoT record
49
+ );
50
+ const sessionId = socket.getSession?.()?.toString("hex").slice(0, 16) ?? "unknown";
51
+ return {
52
+ bindingKey: Buffer.from(bindingKey),
53
+ sessionId,
54
+ };
55
+ }
56
+ /**
57
+ * Verify that a received binding_key matches the expected value
58
+ * for this TLS session and PoT record.
59
+ *
60
+ * This is the normative check from draft-helmprotocol-tttps-02 Section 7.1:
61
+ * "The verifier MUST recompute expected_key via TLS-Exporter
62
+ * and verify it matches the binding_key in the PoT frame header."
63
+ *
64
+ * @param socket - Active TLS socket (same session as sender)
65
+ * @param potWithoutSig - PoT record bytes WITHOUT signature (79 bytes)
66
+ * @param receivedBindingKey - 32-byte binding_key from the received PoT frame
67
+ * @returns true if binding is valid, false if cross-session replay detected
68
+ */
69
+ function verifyBindingKey(socket, potWithoutSig, receivedBindingKey) {
70
+ if (receivedBindingKey.length !== exports.TTTPS_BINDING_KEY_LENGTH) {
71
+ return false;
72
+ }
73
+ const { bindingKey: expected } = computeBindingKey(socket, potWithoutSig);
74
+ // Constant-time comparison (prevents timing attacks)
75
+ return timingSafeEqual(expected, receivedBindingKey);
76
+ }
77
+ /**
78
+ * Constant-time Buffer comparison.
79
+ * Node.js crypto.timingSafeEqual requires equal-length buffers.
80
+ */
81
+ function timingSafeEqual(a, b) {
82
+ if (a.length !== b.length)
83
+ return false;
84
+ const { timingSafeEqual } = require("crypto");
85
+ return timingSafeEqual(a, b);
86
+ }
package/dist/v4_hook.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { EVMConnector } from "./evm_connector";
2
- import { ProtocolFeeCollector } from "./protocol_fee";
3
- import { FeeCalculation } from "./dynamic_fee";
4
2
  import { BeforeSwapParams, AfterSwapParams } from "./types";
3
+ export interface FeeCalculation {
4
+ feeAmount: bigint;
5
+ tier: string;
6
+ }
7
+ export interface ProtocolFeeCollector {
8
+ collectBurnFee(fee: FeeCalculation, signature: string, sender: string, nonce: bigint, deadline: number): Promise<void>;
9
+ }
5
10
  /**
6
11
  * UniswapV4Hook - TTT-based Uniswap V4 Hook Simulation (SDK-side)
7
12
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openttt",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "OpenTTT — TLS-grade transaction ordering for DeFi. Time + Logic + Sync.",
5
5
  "license": "BSL-1.1",
6
6
  "repository": {
@@ -29,6 +29,8 @@
29
29
  }
30
30
  },
31
31
  "files": [
32
+ "dist/adaptive_switch.js",
33
+ "dist/adaptive_switch.d.ts",
32
34
  "dist/ct_log.js",
33
35
  "dist/ct_log.d.ts",
34
36
  "dist/errors.js",
@@ -49,12 +51,18 @@
49
51
  "dist/osnma_source.d.ts",
50
52
  "dist/pool_registry.js",
51
53
  "dist/pool_registry.d.ts",
54
+ "dist/pot_frame.js",
55
+ "dist/pot_frame.d.ts",
52
56
  "dist/pot_signer.js",
53
57
  "dist/pot_signer.d.ts",
58
+ "dist/pot_verifier.js",
59
+ "dist/pot_verifier.d.ts",
54
60
  "dist/signer.js",
55
61
  "dist/signer.d.ts",
56
62
  "dist/time_synthesis.js",
57
63
  "dist/time_synthesis.d.ts",
64
+ "dist/tls_binding.js",
65
+ "dist/tls_binding.d.ts",
58
66
  "dist/trust_store.js",
59
67
  "dist/trust_store.d.ts",
60
68
  "dist/types.js",
@@ -100,6 +108,7 @@
100
108
  "overrides": {
101
109
  "defu": ">=6.1.4",
102
110
  "picomatch": ">=4.0.2",
103
- "handlebars": ">=4.7.9"
111
+ "handlebars": ">=4.7.9",
112
+ "ws": ">=8.21.0"
104
113
  }
105
- }
114
+ }