@teneo-protocol/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +14 -0
- package/.env.test.example +14 -0
- package/.eslintrc.json +26 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude-reviewer.yml +64 -0
- package/.github/workflows/publish-npm.yml +38 -0
- package/.github/workflows/push-to-main.yml +23 -0
- package/.node-version +1 -0
- package/.prettierrc +11 -0
- package/Dockerfile +25 -0
- package/LICENCE +661 -0
- package/README.md +709 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/websocket-client.d.ts +261 -0
- package/dist/core/websocket-client.d.ts.map +1 -0
- package/dist/core/websocket-client.js +875 -0
- package/dist/core/websocket-client.js.map +1 -0
- package/dist/formatters/response-formatter.d.ts +354 -0
- package/dist/formatters/response-formatter.d.ts.map +1 -0
- package/dist/formatters/response-formatter.js +575 -0
- package/dist/formatters/response-formatter.js.map +1 -0
- package/dist/handlers/message-handler-registry.d.ts +155 -0
- package/dist/handlers/message-handler-registry.d.ts.map +1 -0
- package/dist/handlers/message-handler-registry.js +216 -0
- package/dist/handlers/message-handler-registry.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
- package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
- package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
- package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
- package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
- package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/base-handler.js +83 -0
- package/dist/handlers/message-handlers/base-handler.js.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.js +47 -0
- package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.js +29 -0
- package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/index.d.ts +28 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -0
- package/dist/handlers/message-handlers/index.js +100 -0
- package/dist/handlers/message-handlers/index.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.js +44 -0
- package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/types.d.ts +51 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -0
- package/dist/handlers/message-handlers/types.js +7 -0
- package/dist/handlers/message-handlers/types.js.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
- package/dist/handlers/webhook-handler.d.ts +202 -0
- package/dist/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/handlers/webhook-handler.js +511 -0
- package/dist/handlers/webhook-handler.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/agent-registry.d.ts +173 -0
- package/dist/managers/agent-registry.d.ts.map +1 -0
- package/dist/managers/agent-registry.js +310 -0
- package/dist/managers/agent-registry.js.map +1 -0
- package/dist/managers/connection-manager.d.ts +134 -0
- package/dist/managers/connection-manager.d.ts.map +1 -0
- package/dist/managers/connection-manager.js +176 -0
- package/dist/managers/connection-manager.js.map +1 -0
- package/dist/managers/index.d.ts +9 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/managers/index.js +16 -0
- package/dist/managers/index.js.map +1 -0
- package/dist/managers/message-router.d.ts +112 -0
- package/dist/managers/message-router.d.ts.map +1 -0
- package/dist/managers/message-router.js +260 -0
- package/dist/managers/message-router.js.map +1 -0
- package/dist/managers/room-manager.d.ts +165 -0
- package/dist/managers/room-manager.d.ts.map +1 -0
- package/dist/managers/room-manager.js +227 -0
- package/dist/managers/room-manager.js.map +1 -0
- package/dist/teneo-sdk.d.ts +703 -0
- package/dist/teneo-sdk.d.ts.map +1 -0
- package/dist/teneo-sdk.js +907 -0
- package/dist/teneo-sdk.js.map +1 -0
- package/dist/types/config.d.ts +1047 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +720 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/error-codes.d.ts +29 -0
- package/dist/types/error-codes.d.ts.map +1 -0
- package/dist/types/error-codes.js +41 -0
- package/dist/types/error-codes.js.map +1 -0
- package/dist/types/events.d.ts +616 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +261 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +6 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +123 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +3734 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/dist/types/messages.js +482 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/validation.d.ts +81 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +115 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/bounded-queue.d.ts +127 -0
- package/dist/utils/bounded-queue.d.ts.map +1 -0
- package/dist/utils/bounded-queue.js +181 -0
- package/dist/utils/bounded-queue.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +141 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +215 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/deduplication-cache.d.ts +110 -0
- package/dist/utils/deduplication-cache.d.ts.map +1 -0
- package/dist/utils/deduplication-cache.js +177 -0
- package/dist/utils/deduplication-cache.js.map +1 -0
- package/dist/utils/event-waiter.d.ts +101 -0
- package/dist/utils/event-waiter.d.ts.map +1 -0
- package/dist/utils/event-waiter.js +118 -0
- package/dist/utils/event-waiter.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +122 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +190 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry-policy.d.ts +191 -0
- package/dist/utils/retry-policy.d.ts.map +1 -0
- package/dist/utils/retry-policy.js +225 -0
- package/dist/utils/retry-policy.js.map +1 -0
- package/dist/utils/secure-private-key.d.ts +113 -0
- package/dist/utils/secure-private-key.d.ts.map +1 -0
- package/dist/utils/secure-private-key.js +188 -0
- package/dist/utils/secure-private-key.js.map +1 -0
- package/dist/utils/signature-verifier.d.ts +143 -0
- package/dist/utils/signature-verifier.d.ts.map +1 -0
- package/dist/utils/signature-verifier.js +238 -0
- package/dist/utils/signature-verifier.js.map +1 -0
- package/dist/utils/ssrf-validator.d.ts +36 -0
- package/dist/utils/ssrf-validator.d.ts.map +1 -0
- package/dist/utils/ssrf-validator.js +195 -0
- package/dist/utils/ssrf-validator.js.map +1 -0
- package/examples/.env.example +17 -0
- package/examples/basic-usage.ts +211 -0
- package/examples/production-dashboard/.env.example +153 -0
- package/examples/production-dashboard/package.json +39 -0
- package/examples/production-dashboard/public/dashboard.html +642 -0
- package/examples/production-dashboard/server.ts +753 -0
- package/examples/webhook-integration.ts +239 -0
- package/examples/x-influencer-battle-redesign.html +1065 -0
- package/examples/x-influencer-battle-server.ts +217 -0
- package/examples/x-influencer-battle.html +787 -0
- package/package.json +65 -0
- package/src/constants.ts +43 -0
- package/src/core/websocket-client.test.ts +512 -0
- package/src/core/websocket-client.ts +1056 -0
- package/src/formatters/response-formatter.test.ts +571 -0
- package/src/formatters/response-formatter.ts +677 -0
- package/src/handlers/message-handler-registry.ts +239 -0
- package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
- package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
- package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
- package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
- package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
- package/src/handlers/message-handlers/base-handler.ts +101 -0
- package/src/handlers/message-handlers/challenge-handler.ts +57 -0
- package/src/handlers/message-handlers/error-message-handler.ts +27 -0
- package/src/handlers/message-handlers/index.ts +77 -0
- package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
- package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
- package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
- package/src/handlers/message-handlers/task-response-handler.ts +45 -0
- package/src/handlers/message-handlers/types.ts +77 -0
- package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
- package/src/handlers/webhook-handler.test.ts +789 -0
- package/src/handlers/webhook-handler.ts +576 -0
- package/src/index.ts +269 -0
- package/src/managers/agent-registry.test.ts +466 -0
- package/src/managers/agent-registry.ts +347 -0
- package/src/managers/connection-manager.ts +195 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/message-router.ts +349 -0
- package/src/managers/room-manager.ts +248 -0
- package/src/teneo-sdk.ts +1022 -0
- package/src/types/config.test.ts +325 -0
- package/src/types/config.ts +799 -0
- package/src/types/error-codes.ts +44 -0
- package/src/types/events.test.ts +302 -0
- package/src/types/events.ts +382 -0
- package/src/types/health.ts +46 -0
- package/src/types/index.ts +199 -0
- package/src/types/messages.test.ts +660 -0
- package/src/types/messages.ts +570 -0
- package/src/types/validation.ts +123 -0
- package/src/utils/bounded-queue.test.ts +356 -0
- package/src/utils/bounded-queue.ts +205 -0
- package/src/utils/circuit-breaker.test.ts +394 -0
- package/src/utils/circuit-breaker.ts +262 -0
- package/src/utils/deduplication-cache.test.ts +380 -0
- package/src/utils/deduplication-cache.ts +198 -0
- package/src/utils/event-waiter.test.ts +381 -0
- package/src/utils/event-waiter.ts +172 -0
- package/src/utils/index.ts +74 -0
- package/src/utils/logger.ts +87 -0
- package/src/utils/rate-limiter.test.ts +341 -0
- package/src/utils/rate-limiter.ts +211 -0
- package/src/utils/retry-policy.test.ts +558 -0
- package/src/utils/retry-policy.ts +272 -0
- package/src/utils/secure-private-key.test.ts +356 -0
- package/src/utils/secure-private-key.ts +205 -0
- package/src/utils/signature-verifier.test.ts +464 -0
- package/src/utils/signature-verifier.ts +298 -0
- package/src/utils/ssrf-validator.test.ts +372 -0
- package/src/utils/ssrf-validator.ts +224 -0
- package/tests/integration/real-server.test.ts +740 -0
- package/tests/integration/websocket.test.ts +381 -0
- package/tests/integration-setup.ts +16 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +32 -0
- package/vitest.config.ts +42 -0
- package/vitest.integration.config.ts +23 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure private key storage with in-memory encryption
|
|
3
|
+
* Addresses SEC-3: Private Key Exposure Risk
|
|
4
|
+
*
|
|
5
|
+
* This class encrypts private keys in memory using AES-256-GCM to prevent
|
|
6
|
+
* exposure through memory dumps, heap snapshots, or accidental logging.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const secureKey = new SecurePrivateKey(privateKey);
|
|
11
|
+
*
|
|
12
|
+
* // Use the key temporarily for signing
|
|
13
|
+
* const signature = secureKey.use((key) => {
|
|
14
|
+
* return signMessage(key);
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Clean up when done
|
|
18
|
+
* secureKey.destroy();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Securely stores and manages an encrypted private key in memory.
|
|
23
|
+
*
|
|
24
|
+
* The key is encrypted immediately upon construction and only decrypted
|
|
25
|
+
* temporarily when needed for operations like signing. Decrypted keys
|
|
26
|
+
* are zeroed out immediately after use.
|
|
27
|
+
*/
|
|
28
|
+
export declare class SecurePrivateKey {
|
|
29
|
+
private encrypted;
|
|
30
|
+
private encryptionKey;
|
|
31
|
+
private destroyed;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new secure private key storage.
|
|
34
|
+
* The provided private key is encrypted immediately and the original
|
|
35
|
+
* string becomes eligible for garbage collection.
|
|
36
|
+
*
|
|
37
|
+
* @param privateKey - The private key to encrypt and store securely
|
|
38
|
+
* @throws {Error} If private key is empty or invalid
|
|
39
|
+
*/
|
|
40
|
+
constructor(privateKey: string);
|
|
41
|
+
/**
|
|
42
|
+
* Temporarily decrypts the private key and passes it to the provided function.
|
|
43
|
+
* The decrypted key is automatically zeroed out after the function completes,
|
|
44
|
+
* whether it succeeds or throws an error.
|
|
45
|
+
*
|
|
46
|
+
* This is the only way to access the decrypted private key, ensuring minimal
|
|
47
|
+
* exposure time in plaintext.
|
|
48
|
+
*
|
|
49
|
+
* @template T - The return type of the function
|
|
50
|
+
* @param fn - Function that uses the decrypted private key
|
|
51
|
+
* @returns The result of the function
|
|
52
|
+
* @throws {Error} If the key has been destroyed
|
|
53
|
+
* @throws Any error thrown by the provided function
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const account = secureKey.use((key) => privateKeyToAccount(key));
|
|
58
|
+
* const signature = secureKey.use((key) => account.signMessage(key));
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
use<T>(fn: (key: string) => T): T;
|
|
62
|
+
/**
|
|
63
|
+
* Destroys this secure key by zeroing out all sensitive buffers.
|
|
64
|
+
* After calling destroy(), this instance can no longer be used.
|
|
65
|
+
*
|
|
66
|
+
* This should be called when the SDK is disconnected or the key
|
|
67
|
+
* is no longer needed to prevent memory exposure.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* secureKey.destroy();
|
|
72
|
+
* // secureKey.use(...) will now throw an error
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
destroy(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Checks if this secure key has been destroyed.
|
|
78
|
+
*
|
|
79
|
+
* @returns True if destroyed, false otherwise
|
|
80
|
+
*/
|
|
81
|
+
isDestroyed(): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Encrypts the private key using AES-256-GCM.
|
|
84
|
+
*
|
|
85
|
+
* The encrypted buffer format is: [IV (16 bytes) | Auth Tag (16 bytes) | Ciphertext]
|
|
86
|
+
*
|
|
87
|
+
* @param data - The private key to encrypt
|
|
88
|
+
* @returns Buffer containing IV + auth tag + encrypted data
|
|
89
|
+
*/
|
|
90
|
+
private encrypt;
|
|
91
|
+
/**
|
|
92
|
+
* Decrypts the stored private key.
|
|
93
|
+
*
|
|
94
|
+
* @returns The decrypted private key as a string
|
|
95
|
+
* @throws {Error} If decryption fails (tampered data or wrong key)
|
|
96
|
+
*/
|
|
97
|
+
private decrypt;
|
|
98
|
+
/**
|
|
99
|
+
* Best-effort attempt to zero out a string in memory.
|
|
100
|
+
* JavaScript strings are immutable, but we can try to overwrite
|
|
101
|
+
* the backing buffer if it exists.
|
|
102
|
+
*
|
|
103
|
+
* @param str - The string to zero out
|
|
104
|
+
*/
|
|
105
|
+
private zeroOutString;
|
|
106
|
+
/**
|
|
107
|
+
* Checks if this instance has been destroyed and throws if so.
|
|
108
|
+
*
|
|
109
|
+
* @throws {Error} If the key has been destroyed
|
|
110
|
+
*/
|
|
111
|
+
private checkNotDestroyed;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=secure-private-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-private-key.d.ts","sourceRoot":"","sources":["../../src/utils/secure-private-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;;;OAOG;gBACS,UAAU,EAAE,MAAM;IAc9B;;;;;;;;;;;;;;;;;;;OAmBG;IACI,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC;IAcxC;;;;;;;;;;;;OAYG;IACI,OAAO,IAAI,IAAI;IAYtB;;;;OAIG;IACI,WAAW,IAAI,OAAO;IAI7B;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO;IAoBf;;;;;OAKG;IACH,OAAO,CAAC,OAAO;IAmBf;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IASrB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;CAK1B"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Secure private key storage with in-memory encryption
|
|
4
|
+
* Addresses SEC-3: Private Key Exposure Risk
|
|
5
|
+
*
|
|
6
|
+
* This class encrypts private keys in memory using AES-256-GCM to prevent
|
|
7
|
+
* exposure through memory dumps, heap snapshots, or accidental logging.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const secureKey = new SecurePrivateKey(privateKey);
|
|
12
|
+
*
|
|
13
|
+
* // Use the key temporarily for signing
|
|
14
|
+
* const signature = secureKey.use((key) => {
|
|
15
|
+
* return signMessage(key);
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Clean up when done
|
|
19
|
+
* secureKey.destroy();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.SecurePrivateKey = void 0;
|
|
27
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
28
|
+
/**
|
|
29
|
+
* Securely stores and manages an encrypted private key in memory.
|
|
30
|
+
*
|
|
31
|
+
* The key is encrypted immediately upon construction and only decrypted
|
|
32
|
+
* temporarily when needed for operations like signing. Decrypted keys
|
|
33
|
+
* are zeroed out immediately after use.
|
|
34
|
+
*/
|
|
35
|
+
class SecurePrivateKey {
|
|
36
|
+
/**
|
|
37
|
+
* Creates a new secure private key storage.
|
|
38
|
+
* The provided private key is encrypted immediately and the original
|
|
39
|
+
* string becomes eligible for garbage collection.
|
|
40
|
+
*
|
|
41
|
+
* @param privateKey - The private key to encrypt and store securely
|
|
42
|
+
* @throws {Error} If private key is empty or invalid
|
|
43
|
+
*/
|
|
44
|
+
constructor(privateKey) {
|
|
45
|
+
this.destroyed = false;
|
|
46
|
+
if (!privateKey || typeof privateKey !== 'string') {
|
|
47
|
+
throw new Error('Private key must be a non-empty string');
|
|
48
|
+
}
|
|
49
|
+
// Generate a random encryption key for AES-256
|
|
50
|
+
this.encryptionKey = crypto_1.default.randomBytes(32);
|
|
51
|
+
// Encrypt the private key immediately
|
|
52
|
+
this.encrypted = this.encrypt(privateKey);
|
|
53
|
+
// Original privateKey string will be garbage collected
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Temporarily decrypts the private key and passes it to the provided function.
|
|
57
|
+
* The decrypted key is automatically zeroed out after the function completes,
|
|
58
|
+
* whether it succeeds or throws an error.
|
|
59
|
+
*
|
|
60
|
+
* This is the only way to access the decrypted private key, ensuring minimal
|
|
61
|
+
* exposure time in plaintext.
|
|
62
|
+
*
|
|
63
|
+
* @template T - The return type of the function
|
|
64
|
+
* @param fn - Function that uses the decrypted private key
|
|
65
|
+
* @returns The result of the function
|
|
66
|
+
* @throws {Error} If the key has been destroyed
|
|
67
|
+
* @throws Any error thrown by the provided function
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const account = secureKey.use((key) => privateKeyToAccount(key));
|
|
72
|
+
* const signature = secureKey.use((key) => account.signMessage(key));
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
use(fn) {
|
|
76
|
+
this.checkNotDestroyed();
|
|
77
|
+
const decrypted = this.decrypt();
|
|
78
|
+
try {
|
|
79
|
+
return fn(decrypted);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
// Zero out the decrypted string in memory
|
|
83
|
+
// Note: This is best-effort as JavaScript strings are immutable
|
|
84
|
+
// but we overwrite the backing buffer
|
|
85
|
+
this.zeroOutString(decrypted);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Destroys this secure key by zeroing out all sensitive buffers.
|
|
90
|
+
* After calling destroy(), this instance can no longer be used.
|
|
91
|
+
*
|
|
92
|
+
* This should be called when the SDK is disconnected or the key
|
|
93
|
+
* is no longer needed to prevent memory exposure.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* secureKey.destroy();
|
|
98
|
+
* // secureKey.use(...) will now throw an error
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
destroy() {
|
|
102
|
+
if (this.destroyed) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Zero out all sensitive buffers
|
|
106
|
+
this.encryptionKey.fill(0);
|
|
107
|
+
this.encrypted.fill(0);
|
|
108
|
+
this.destroyed = true;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Checks if this secure key has been destroyed.
|
|
112
|
+
*
|
|
113
|
+
* @returns True if destroyed, false otherwise
|
|
114
|
+
*/
|
|
115
|
+
isDestroyed() {
|
|
116
|
+
return this.destroyed;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Encrypts the private key using AES-256-GCM.
|
|
120
|
+
*
|
|
121
|
+
* The encrypted buffer format is: [IV (16 bytes) | Auth Tag (16 bytes) | Ciphertext]
|
|
122
|
+
*
|
|
123
|
+
* @param data - The private key to encrypt
|
|
124
|
+
* @returns Buffer containing IV + auth tag + encrypted data
|
|
125
|
+
*/
|
|
126
|
+
encrypt(data) {
|
|
127
|
+
// Generate random initialization vector
|
|
128
|
+
const iv = crypto_1.default.randomBytes(16);
|
|
129
|
+
// Create cipher
|
|
130
|
+
const cipher = crypto_1.default.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
|
|
131
|
+
// Encrypt the data
|
|
132
|
+
const encrypted = Buffer.concat([
|
|
133
|
+
cipher.update(data, 'utf8'),
|
|
134
|
+
cipher.final()
|
|
135
|
+
]);
|
|
136
|
+
// Get authentication tag for integrity verification
|
|
137
|
+
const authTag = cipher.getAuthTag();
|
|
138
|
+
// Combine IV + authTag + encrypted data
|
|
139
|
+
return Buffer.concat([iv, authTag, encrypted]);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Decrypts the stored private key.
|
|
143
|
+
*
|
|
144
|
+
* @returns The decrypted private key as a string
|
|
145
|
+
* @throws {Error} If decryption fails (tampered data or wrong key)
|
|
146
|
+
*/
|
|
147
|
+
decrypt() {
|
|
148
|
+
// Extract components from encrypted buffer
|
|
149
|
+
const iv = this.encrypted.subarray(0, 16);
|
|
150
|
+
const authTag = this.encrypted.subarray(16, 32);
|
|
151
|
+
const ciphertext = this.encrypted.subarray(32);
|
|
152
|
+
// Create decipher
|
|
153
|
+
const decipher = crypto_1.default.createDecipheriv('aes-256-gcm', this.encryptionKey, iv);
|
|
154
|
+
decipher.setAuthTag(authTag);
|
|
155
|
+
// Decrypt the data
|
|
156
|
+
const decrypted = Buffer.concat([
|
|
157
|
+
decipher.update(ciphertext),
|
|
158
|
+
decipher.final()
|
|
159
|
+
]);
|
|
160
|
+
return decrypted.toString('utf8');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Best-effort attempt to zero out a string in memory.
|
|
164
|
+
* JavaScript strings are immutable, but we can try to overwrite
|
|
165
|
+
* the backing buffer if it exists.
|
|
166
|
+
*
|
|
167
|
+
* @param str - The string to zero out
|
|
168
|
+
*/
|
|
169
|
+
zeroOutString(str) {
|
|
170
|
+
// Convert to buffer and zero it out
|
|
171
|
+
const buffer = Buffer.from(str, 'utf8');
|
|
172
|
+
buffer.fill(0);
|
|
173
|
+
// Note: The original string object may still exist in memory
|
|
174
|
+
// until garbage collected, but this reduces the attack surface
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Checks if this instance has been destroyed and throws if so.
|
|
178
|
+
*
|
|
179
|
+
* @throws {Error} If the key has been destroyed
|
|
180
|
+
*/
|
|
181
|
+
checkNotDestroyed() {
|
|
182
|
+
if (this.destroyed) {
|
|
183
|
+
throw new Error('SecurePrivateKey has been destroyed and can no longer be used');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.SecurePrivateKey = SecurePrivateKey;
|
|
188
|
+
//# sourceMappingURL=secure-private-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-private-key.js","sourceRoot":"","sources":["../../src/utils/secure-private-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature Verifier for Message Authentication
|
|
3
|
+
* Provides cryptographic verification of message signatures using Ethereum ECDSA
|
|
4
|
+
*/
|
|
5
|
+
import { type Address } from 'viem';
|
|
6
|
+
import { BaseMessage, MessageType } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Options for signature verification
|
|
9
|
+
*/
|
|
10
|
+
export interface SignatureVerificationOptions {
|
|
11
|
+
/** Whitelist of trusted agent addresses (empty = allow all) */
|
|
12
|
+
trustedAddresses?: Address[];
|
|
13
|
+
/** Message types that require signatures */
|
|
14
|
+
requireSignaturesFor?: MessageType[];
|
|
15
|
+
/** Reject messages with missing signatures (vs just warn) */
|
|
16
|
+
strictMode?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Result of signature verification
|
|
20
|
+
*/
|
|
21
|
+
export interface VerificationResult {
|
|
22
|
+
/** Whether the signature is valid */
|
|
23
|
+
valid: boolean;
|
|
24
|
+
/** Recovered address from signature (if signature present) */
|
|
25
|
+
recoveredAddress?: Address;
|
|
26
|
+
/** Reason for failure (if not valid) */
|
|
27
|
+
reason?: string;
|
|
28
|
+
/** Whether signature was missing */
|
|
29
|
+
signatureMissing: boolean;
|
|
30
|
+
/** Whether address is in trusted whitelist (if whitelist configured) */
|
|
31
|
+
isTrusted?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Signature verifier for authenticating message origins
|
|
35
|
+
* Prevents spoofing attacks by verifying Ethereum signatures on messages
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const verifier = new SignatureVerifier({
|
|
40
|
+
* trustedAddresses: ['0x123...', '0x456...'],
|
|
41
|
+
* requireSignaturesFor: ['task_response', 'agent_selected'],
|
|
42
|
+
* strictMode: true
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* const result = await verifier.verify(message);
|
|
46
|
+
* if (!result.valid) {
|
|
47
|
+
* console.log(`Verification failed: ${result.reason}`);
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare class SignatureVerifier {
|
|
52
|
+
private readonly options;
|
|
53
|
+
/**
|
|
54
|
+
* Creates a new signature verifier
|
|
55
|
+
*
|
|
56
|
+
* @param options - Verification options
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const verifier = new SignatureVerifier({
|
|
61
|
+
* trustedAddresses: ['0x123...'],
|
|
62
|
+
* requireSignaturesFor: ['task_response'],
|
|
63
|
+
* strictMode: false
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
constructor(options?: SignatureVerificationOptions);
|
|
68
|
+
/**
|
|
69
|
+
* Verify a message's signature
|
|
70
|
+
*
|
|
71
|
+
* @param message - The message to verify
|
|
72
|
+
* @returns Verification result with validity and recovered address
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const result = await verifier.verify(message);
|
|
77
|
+
* if (result.valid) {
|
|
78
|
+
* console.log(`Valid signature from ${result.recoveredAddress}`);
|
|
79
|
+
* } else {
|
|
80
|
+
* console.log(`Invalid: ${result.reason}`);
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
verify(message: BaseMessage): Promise<VerificationResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Create a canonical hash of message content for signing
|
|
87
|
+
*
|
|
88
|
+
* @param content - The signable content object
|
|
89
|
+
* @returns Message hash string
|
|
90
|
+
*/
|
|
91
|
+
createMessageHash(content: object): string;
|
|
92
|
+
/**
|
|
93
|
+
* Extract signable content from message
|
|
94
|
+
* Excludes signature and publicKey fields to prevent circular dependency
|
|
95
|
+
*
|
|
96
|
+
* @param message - The message to extract content from
|
|
97
|
+
* @returns Object containing signable fields
|
|
98
|
+
*/
|
|
99
|
+
getSignableContent(message: BaseMessage): object;
|
|
100
|
+
/**
|
|
101
|
+
* Check if signature is required for a message type
|
|
102
|
+
*
|
|
103
|
+
* @param messageType - The type of message
|
|
104
|
+
* @returns True if signature is required
|
|
105
|
+
*/
|
|
106
|
+
isSignatureRequired(messageType: MessageType): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Check if an address is in the trusted whitelist
|
|
109
|
+
*
|
|
110
|
+
* @param address - The address to check
|
|
111
|
+
* @returns True if address is trusted (or no whitelist configured)
|
|
112
|
+
*/
|
|
113
|
+
isTrustedAddress(address: Address): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Get the address to verify against
|
|
116
|
+
* Tries publicKey first, falls back to 'from' field
|
|
117
|
+
*
|
|
118
|
+
* @param message - The message to get address from
|
|
119
|
+
* @returns Address to verify, or undefined if none available
|
|
120
|
+
*/
|
|
121
|
+
private getVerificationAddress;
|
|
122
|
+
/**
|
|
123
|
+
* Update verification options
|
|
124
|
+
*
|
|
125
|
+
* @param options - New options (merged with existing)
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* verifier.updateOptions({
|
|
130
|
+
* trustedAddresses: ['0x789...'],
|
|
131
|
+
* strictMode: true
|
|
132
|
+
* });
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
updateOptions(options: Partial<SignatureVerificationOptions>): void;
|
|
136
|
+
/**
|
|
137
|
+
* Get current verification options
|
|
138
|
+
*
|
|
139
|
+
* @returns Copy of current options
|
|
140
|
+
*/
|
|
141
|
+
getOptions(): SignatureVerificationOptions;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=signature-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-verifier.d.ts","sourceRoot":"","sources":["../../src/utils/signature-verifier.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA8B,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,OAAO,EAAE,CAAC;IAE7B,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,WAAW,EAAE,CAAC;IAErC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAC;IAEf,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,oCAAoC;IACpC,gBAAgB,EAAE,OAAO,CAAC;IAE1B,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IAEjE;;;;;;;;;;;;;OAaG;gBACS,OAAO,GAAE,4BAAiC;IAQtD;;;;;;;;;;;;;;;OAeG;IACU,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8EtE;;;;;OAKG;IACI,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQjD;;;;;;OAMG;IACI,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM;IAcvD;;;;;OAKG;IACI,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI7D;;;;;OAKG;IACI,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAUlD;;;;;;OAMG;YACW,sBAAsB;IAiBpC;;;;;;;;;;;;OAYG;IACI,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,GAAG,IAAI;IAY1E;;;;OAIG;IACI,UAAU,IAAI,4BAA4B;CAGlD"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Signature Verifier for Message Authentication
|
|
4
|
+
* Provides cryptographic verification of message signatures using Ethereum ECDSA
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SignatureVerifier = void 0;
|
|
8
|
+
const viem_1 = require("viem");
|
|
9
|
+
/**
|
|
10
|
+
* Signature verifier for authenticating message origins
|
|
11
|
+
* Prevents spoofing attacks by verifying Ethereum signatures on messages
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const verifier = new SignatureVerifier({
|
|
16
|
+
* trustedAddresses: ['0x123...', '0x456...'],
|
|
17
|
+
* requireSignaturesFor: ['task_response', 'agent_selected'],
|
|
18
|
+
* strictMode: true
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* const result = await verifier.verify(message);
|
|
22
|
+
* if (!result.valid) {
|
|
23
|
+
* console.log(`Verification failed: ${result.reason}`);
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
class SignatureVerifier {
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new signature verifier
|
|
30
|
+
*
|
|
31
|
+
* @param options - Verification options
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const verifier = new SignatureVerifier({
|
|
36
|
+
* trustedAddresses: ['0x123...'],
|
|
37
|
+
* requireSignaturesFor: ['task_response'],
|
|
38
|
+
* strictMode: false
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.options = {
|
|
44
|
+
trustedAddresses: options.trustedAddresses || [],
|
|
45
|
+
requireSignaturesFor: options.requireSignaturesFor || [],
|
|
46
|
+
strictMode: options.strictMode !== undefined ? options.strictMode : false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Verify a message's signature
|
|
51
|
+
*
|
|
52
|
+
* @param message - The message to verify
|
|
53
|
+
* @returns Verification result with validity and recovered address
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = await verifier.verify(message);
|
|
58
|
+
* if (result.valid) {
|
|
59
|
+
* console.log(`Valid signature from ${result.recoveredAddress}`);
|
|
60
|
+
* } else {
|
|
61
|
+
* console.log(`Invalid: ${result.reason}`);
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
async verify(message) {
|
|
66
|
+
// Check if signature is present
|
|
67
|
+
if (!message.signature) {
|
|
68
|
+
const isRequired = this.isSignatureRequired(message.type);
|
|
69
|
+
return {
|
|
70
|
+
valid: !isRequired && !this.options.strictMode,
|
|
71
|
+
signatureMissing: true,
|
|
72
|
+
reason: isRequired
|
|
73
|
+
? `Signature required for message type '${message.type}'`
|
|
74
|
+
: 'Signature missing but not required'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Extract signable content (excludes signature and publicKey fields)
|
|
78
|
+
const signableContent = this.getSignableContent(message);
|
|
79
|
+
// Create canonical message hash
|
|
80
|
+
const messageHash = this.createMessageHash(signableContent);
|
|
81
|
+
try {
|
|
82
|
+
// Determine which address to verify against
|
|
83
|
+
const addressToVerify = await this.getVerificationAddress(message);
|
|
84
|
+
if (!addressToVerify) {
|
|
85
|
+
return {
|
|
86
|
+
valid: false,
|
|
87
|
+
signatureMissing: false,
|
|
88
|
+
reason: 'No address available for verification (missing publicKey and from fields)'
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Verify signature using viem
|
|
92
|
+
const isValid = await (0, viem_1.verifyMessage)({
|
|
93
|
+
address: addressToVerify,
|
|
94
|
+
message: messageHash,
|
|
95
|
+
signature: message.signature
|
|
96
|
+
});
|
|
97
|
+
if (!isValid) {
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
recoveredAddress: addressToVerify,
|
|
101
|
+
signatureMissing: false,
|
|
102
|
+
reason: 'Signature verification failed - signature does not match message content'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// Check if address is trusted (if whitelist configured)
|
|
106
|
+
const isTrusted = this.isTrustedAddress(addressToVerify);
|
|
107
|
+
// If whitelist is configured and address is not trusted, reject
|
|
108
|
+
if (this.options.trustedAddresses.length > 0 && !isTrusted) {
|
|
109
|
+
return {
|
|
110
|
+
valid: false,
|
|
111
|
+
recoveredAddress: addressToVerify,
|
|
112
|
+
signatureMissing: false,
|
|
113
|
+
isTrusted: false,
|
|
114
|
+
reason: `Address ${addressToVerify} is not in trusted whitelist`
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
valid: true,
|
|
119
|
+
recoveredAddress: addressToVerify,
|
|
120
|
+
signatureMissing: false,
|
|
121
|
+
isTrusted
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
valid: false,
|
|
127
|
+
signatureMissing: false,
|
|
128
|
+
reason: `Signature verification error: ${error instanceof Error ? error.message : String(error)}`
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Create a canonical hash of message content for signing
|
|
134
|
+
*
|
|
135
|
+
* @param content - The signable content object
|
|
136
|
+
* @returns Message hash string
|
|
137
|
+
*/
|
|
138
|
+
createMessageHash(content) {
|
|
139
|
+
// Create canonical JSON string (sorted keys for consistency)
|
|
140
|
+
const canonical = JSON.stringify(content, Object.keys(content).sort());
|
|
141
|
+
// Use viem's hashMessage for Ethereum-compatible hashing
|
|
142
|
+
return (0, viem_1.hashMessage)(canonical);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Extract signable content from message
|
|
146
|
+
* Excludes signature and publicKey fields to prevent circular dependency
|
|
147
|
+
*
|
|
148
|
+
* @param message - The message to extract content from
|
|
149
|
+
* @returns Object containing signable fields
|
|
150
|
+
*/
|
|
151
|
+
getSignableContent(message) {
|
|
152
|
+
const { signature, publicKey, id, ...signableContent } = message;
|
|
153
|
+
// Include only defined fields for consistent hashing
|
|
154
|
+
const filtered = {};
|
|
155
|
+
for (const [key, value] of Object.entries(signableContent)) {
|
|
156
|
+
if (value !== undefined) {
|
|
157
|
+
filtered[key] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return filtered;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Check if signature is required for a message type
|
|
164
|
+
*
|
|
165
|
+
* @param messageType - The type of message
|
|
166
|
+
* @returns True if signature is required
|
|
167
|
+
*/
|
|
168
|
+
isSignatureRequired(messageType) {
|
|
169
|
+
return this.options.requireSignaturesFor.includes(messageType);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if an address is in the trusted whitelist
|
|
173
|
+
*
|
|
174
|
+
* @param address - The address to check
|
|
175
|
+
* @returns True if address is trusted (or no whitelist configured)
|
|
176
|
+
*/
|
|
177
|
+
isTrustedAddress(address) {
|
|
178
|
+
if (this.options.trustedAddresses.length === 0) {
|
|
179
|
+
return true; // No whitelist = trust all
|
|
180
|
+
}
|
|
181
|
+
return this.options.trustedAddresses.some(trusted => trusted.toLowerCase() === address.toLowerCase());
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get the address to verify against
|
|
185
|
+
* Tries publicKey first, falls back to 'from' field
|
|
186
|
+
*
|
|
187
|
+
* @param message - The message to get address from
|
|
188
|
+
* @returns Address to verify, or undefined if none available
|
|
189
|
+
*/
|
|
190
|
+
async getVerificationAddress(message) {
|
|
191
|
+
// If message includes publicKey, derive address from it
|
|
192
|
+
if (message.publicKey) {
|
|
193
|
+
// publicKey should be an Ethereum address already
|
|
194
|
+
// If it's actually a public key, we'd need to derive the address
|
|
195
|
+
// For now, assume publicKey field contains the address
|
|
196
|
+
return message.publicKey;
|
|
197
|
+
}
|
|
198
|
+
// Fall back to 'from' field if it looks like an address
|
|
199
|
+
if (message.from && message.from.startsWith('0x') && message.from.length === 42) {
|
|
200
|
+
return message.from;
|
|
201
|
+
}
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Update verification options
|
|
206
|
+
*
|
|
207
|
+
* @param options - New options (merged with existing)
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* verifier.updateOptions({
|
|
212
|
+
* trustedAddresses: ['0x789...'],
|
|
213
|
+
* strictMode: true
|
|
214
|
+
* });
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
updateOptions(options) {
|
|
218
|
+
if (options.trustedAddresses !== undefined) {
|
|
219
|
+
this.options.trustedAddresses = options.trustedAddresses;
|
|
220
|
+
}
|
|
221
|
+
if (options.requireSignaturesFor !== undefined) {
|
|
222
|
+
this.options.requireSignaturesFor = options.requireSignaturesFor;
|
|
223
|
+
}
|
|
224
|
+
if (options.strictMode !== undefined) {
|
|
225
|
+
this.options.strictMode = options.strictMode;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get current verification options
|
|
230
|
+
*
|
|
231
|
+
* @returns Copy of current options
|
|
232
|
+
*/
|
|
233
|
+
getOptions() {
|
|
234
|
+
return { ...this.options };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.SignatureVerifier = SignatureVerifier;
|
|
238
|
+
//# sourceMappingURL=signature-verifier.js.map
|