@bananalink-sdk/protocol 1.2.8 → 1.4.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/dist/{chunk-32OWUOZ3.js → chunk-KJ7QIHAY.js} +11 -7
- package/dist/chunk-KJ7QIHAY.js.map +1 -0
- package/dist/{chunk-VXLUSU5B.cjs → chunk-MUYKP6UQ.cjs} +63 -8
- package/dist/chunk-MUYKP6UQ.cjs.map +1 -0
- package/dist/{chunk-MCZG7QEM.cjs → chunk-NGPP7HUR.cjs} +11 -7
- package/dist/chunk-NGPP7HUR.cjs.map +1 -0
- package/dist/{chunk-LELPCIE7.js → chunk-OBJR2TL4.js} +54 -4
- package/dist/chunk-OBJR2TL4.js.map +1 -0
- package/dist/{chunk-KNGZKGRS.cjs → chunk-RZPN2GDJ.cjs} +13 -4
- package/dist/chunk-RZPN2GDJ.cjs.map +1 -0
- package/dist/{chunk-7KYDLL3B.js → chunk-XCMAKN3P.js} +13 -5
- package/dist/chunk-XCMAKN3P.js.map +1 -0
- package/dist/{client-session-claim-C4lUik3b.d.cts → client-session-claim-CkRKTG50.d.cts} +12 -2
- package/dist/{client-session-claim-3QF3noOr.d.ts → client-session-claim-CrIDASkZ.d.ts} +12 -2
- package/dist/crypto/providers/noble-provider.cjs +2 -3
- package/dist/crypto/providers/noble-provider.d.cts +0 -7
- package/dist/crypto/providers/noble-provider.d.ts +0 -7
- package/dist/crypto/providers/noble-provider.js +1 -2
- package/dist/crypto/providers/node-provider.cjs +7 -29
- package/dist/crypto/providers/node-provider.cjs.map +1 -1
- package/dist/crypto/providers/node-provider.d.cts +0 -7
- package/dist/crypto/providers/node-provider.d.ts +0 -7
- package/dist/crypto/providers/node-provider.js +7 -29
- package/dist/crypto/providers/node-provider.js.map +1 -1
- package/dist/crypto/providers/quickcrypto-provider.cjs +8 -46
- package/dist/crypto/providers/quickcrypto-provider.cjs.map +1 -1
- package/dist/crypto/providers/quickcrypto-provider.d.cts +0 -9
- package/dist/crypto/providers/quickcrypto-provider.d.ts +0 -9
- package/dist/crypto/providers/quickcrypto-provider.js +7 -45
- package/dist/crypto/providers/quickcrypto-provider.js.map +1 -1
- package/dist/crypto/providers/webcrypto-provider.cjs +0 -2
- package/dist/crypto/providers/webcrypto-provider.cjs.map +1 -1
- package/dist/crypto/providers/webcrypto-provider.d.cts +0 -7
- package/dist/crypto/providers/webcrypto-provider.d.ts +0 -7
- package/dist/crypto/providers/webcrypto-provider.js +0 -2
- package/dist/crypto/providers/webcrypto-provider.js.map +1 -1
- package/dist/{crypto-BUS06Qz-.d.cts → crypto-BK0Ile6V.d.cts} +1 -1
- package/dist/{crypto-BUS06Qz-.d.ts → crypto-BK0Ile6V.d.ts} +1 -1
- package/dist/crypto-export.cjs +50 -51
- package/dist/crypto-export.cjs.map +1 -1
- package/dist/crypto-export.d.cts +1 -1
- package/dist/crypto-export.d.ts +1 -1
- package/dist/crypto-export.js +2 -4
- package/dist/crypto-export.js.map +1 -1
- package/dist/index.cjs +8 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -4
- package/dist/index.d.ts +31 -4
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/schemas-export.cjs +76 -72
- package/dist/schemas-export.d.cts +116 -1
- package/dist/schemas-export.d.ts +116 -1
- package/dist/schemas-export.js +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/validation-export.cjs +76 -72
- package/dist/validation-export.d.cts +1 -1
- package/dist/validation-export.d.ts +1 -1
- package/dist/validation-export.js +1 -1
- package/package.json +1 -1
- package/src/crypto/providers/noble-provider.ts +44 -49
- package/src/crypto/providers/node-provider.ts +18 -59
- package/src/crypto/providers/quickcrypto-provider.ts +25 -84
- package/src/crypto/providers/registry.ts +14 -9
- package/src/crypto/providers/webcrypto-provider.ts +28 -43
- package/src/index.ts +1 -0
- package/src/schemas/client-messages.ts +14 -0
- package/src/schemas/wallet-messages.ts +4 -0
- package/src/types/client-messages.ts +26 -1
- package/src/types/index.ts +9 -0
- package/src/types/persistence.ts +32 -0
- package/src/types/wallet-messages.ts +6 -2
- package/dist/chunk-32OWUOZ3.js.map +0 -1
- package/dist/chunk-7KYDLL3B.js.map +0 -1
- package/dist/chunk-A6FLEJ7R.cjs +0 -62
- package/dist/chunk-A6FLEJ7R.cjs.map +0 -1
- package/dist/chunk-KNGZKGRS.cjs.map +0 -1
- package/dist/chunk-LELPCIE7.js.map +0 -1
- package/dist/chunk-MCZG7QEM.cjs.map +0 -1
- package/dist/chunk-TCVKC227.js +0 -56
- package/dist/chunk-TCVKC227.js.map +0 -1
- package/dist/chunk-VXLUSU5B.cjs.map +0 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Logger } from '@bananalink-sdk/logger';
|
|
2
2
|
import type { CryptoProvider, CryptoKeyLike, ProviderKeyPair } from '../../types/crypto-provider';
|
|
3
3
|
import { NobleCryptoProvider } from './noble-provider';
|
|
4
|
-
import { registerCryptoProvider } from './registry';
|
|
5
4
|
|
|
6
5
|
// Global require function type for React Native environments
|
|
7
6
|
type RequireFunction = (id: string) => unknown;
|
|
@@ -51,7 +50,7 @@ class QuickCryptoKey implements CryptoKeyLike {
|
|
|
51
50
|
public readonly type: 'public' | 'private' | 'secret',
|
|
52
51
|
public readonly algorithm: string,
|
|
53
52
|
public readonly extractable: boolean = true,
|
|
54
|
-
public readonly usages: readonly string[] = []
|
|
53
|
+
public readonly usages: readonly string[] = [],
|
|
55
54
|
) {}
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -110,8 +109,8 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
110
109
|
this.logger?.error('Failed to initialize QuickCrypto', {
|
|
111
110
|
error: {
|
|
112
111
|
message: error instanceof Error ? error.message : String(error),
|
|
113
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
114
|
-
}
|
|
112
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
113
|
+
},
|
|
115
114
|
});
|
|
116
115
|
// Mark as unavailable if we can't actually use it
|
|
117
116
|
(this as { isAvailable: boolean }).isAvailable = false;
|
|
@@ -137,20 +136,8 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
return {
|
|
140
|
-
privateKey: new QuickCryptoKey(
|
|
141
|
-
|
|
142
|
-
'private',
|
|
143
|
-
'ECDH-P256',
|
|
144
|
-
true,
|
|
145
|
-
['deriveKey']
|
|
146
|
-
),
|
|
147
|
-
publicKey: new QuickCryptoKey(
|
|
148
|
-
nobleKeyPair.publicKey.data,
|
|
149
|
-
'public',
|
|
150
|
-
'ECDH-P256',
|
|
151
|
-
true,
|
|
152
|
-
[]
|
|
153
|
-
)
|
|
139
|
+
privateKey: new QuickCryptoKey(nobleKeyPair.privateKey.data, 'private', 'ECDH-P256', true, ['deriveKey']),
|
|
140
|
+
publicKey: new QuickCryptoKey(nobleKeyPair.publicKey.data, 'public', 'ECDH-P256', true, []),
|
|
154
141
|
};
|
|
155
142
|
}
|
|
156
143
|
|
|
@@ -179,13 +166,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
179
166
|
}
|
|
180
167
|
|
|
181
168
|
// Wrap in QuickCryptoKey for consistency
|
|
182
|
-
return new QuickCryptoKey(
|
|
183
|
-
nobleKey.data,
|
|
184
|
-
'public',
|
|
185
|
-
'ECDH-P256',
|
|
186
|
-
true,
|
|
187
|
-
[]
|
|
188
|
-
);
|
|
169
|
+
return new QuickCryptoKey(nobleKey.data, 'public', 'ECDH-P256', true, []);
|
|
189
170
|
}
|
|
190
171
|
|
|
191
172
|
/**
|
|
@@ -199,13 +180,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
199
180
|
}
|
|
200
181
|
|
|
201
182
|
// Wrap in QuickCryptoKey for consistency
|
|
202
|
-
return new QuickCryptoKey(
|
|
203
|
-
nobleKey.data,
|
|
204
|
-
'private',
|
|
205
|
-
'ECDH-P256',
|
|
206
|
-
true,
|
|
207
|
-
['deriveKey']
|
|
208
|
-
);
|
|
183
|
+
return new QuickCryptoKey(nobleKey.data, 'private', 'ECDH-P256', true, ['deriveKey']);
|
|
209
184
|
}
|
|
210
185
|
|
|
211
186
|
/**
|
|
@@ -221,13 +196,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
221
196
|
}
|
|
222
197
|
|
|
223
198
|
// Wrap in QuickCryptoKey for consistency
|
|
224
|
-
return new QuickCryptoKey(
|
|
225
|
-
sharedSecret.data,
|
|
226
|
-
'secret',
|
|
227
|
-
'AES-GCM',
|
|
228
|
-
true,
|
|
229
|
-
['encrypt', 'decrypt']
|
|
230
|
-
);
|
|
199
|
+
return new QuickCryptoKey(sharedSecret.data, 'secret', 'AES-GCM', true, ['encrypt', 'decrypt']);
|
|
231
200
|
}
|
|
232
201
|
|
|
233
202
|
/**
|
|
@@ -241,13 +210,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
241
210
|
}
|
|
242
211
|
|
|
243
212
|
// Wrap in QuickCryptoKey for consistency
|
|
244
|
-
return new QuickCryptoKey(
|
|
245
|
-
encryptionKey.data,
|
|
246
|
-
'secret',
|
|
247
|
-
'AES-GCM',
|
|
248
|
-
true,
|
|
249
|
-
['encrypt', 'decrypt']
|
|
250
|
-
);
|
|
213
|
+
return new QuickCryptoKey(encryptionKey.data, 'secret', 'AES-GCM', true, ['encrypt', 'decrypt']);
|
|
251
214
|
}
|
|
252
215
|
|
|
253
216
|
/**
|
|
@@ -265,8 +228,8 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
265
228
|
} catch (error) {
|
|
266
229
|
this.logger?.error('QuickCrypto randomBytes failed, falling back to Noble', {
|
|
267
230
|
error: {
|
|
268
|
-
message: error instanceof Error ? error.message : String(error)
|
|
269
|
-
}
|
|
231
|
+
message: error instanceof Error ? error.message : String(error),
|
|
232
|
+
},
|
|
270
233
|
});
|
|
271
234
|
return this.nobleProvider.randomBytes(length);
|
|
272
235
|
}
|
|
@@ -283,7 +246,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
283
246
|
|
|
284
247
|
this.logger?.debug('Encrypting data with AES-GCM using QuickCrypto', {
|
|
285
248
|
dataSize: data.byteLength,
|
|
286
|
-
ivSize: iv.byteLength
|
|
249
|
+
ivSize: iv.byteLength,
|
|
287
250
|
});
|
|
288
251
|
|
|
289
252
|
const secretKey = key as QuickCryptoKey;
|
|
@@ -291,7 +254,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
291
254
|
if (secretKey.type !== 'secret') {
|
|
292
255
|
const error = new Error('Expected secret key');
|
|
293
256
|
this.logger?.error('Encryption failed - invalid key type', {
|
|
294
|
-
actualType: secretKey.type
|
|
257
|
+
actualType: secretKey.type,
|
|
295
258
|
});
|
|
296
259
|
throw error;
|
|
297
260
|
}
|
|
@@ -313,7 +276,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
313
276
|
result.set(authTag, encrypted.length + final.length);
|
|
314
277
|
|
|
315
278
|
this.logger?.debug('Encryption completed using QuickCrypto', {
|
|
316
|
-
ciphertextSize: result.byteLength
|
|
279
|
+
ciphertextSize: result.byteLength,
|
|
317
280
|
});
|
|
318
281
|
|
|
319
282
|
return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
|
|
@@ -321,8 +284,8 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
321
284
|
this.logger?.error('QuickCrypto encryption failed, falling back to Noble', {
|
|
322
285
|
error: {
|
|
323
286
|
message: error instanceof Error ? error.message : String(error),
|
|
324
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
325
|
-
}
|
|
287
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
288
|
+
},
|
|
326
289
|
});
|
|
327
290
|
return this.nobleProvider.encrypt(key, data, iv);
|
|
328
291
|
}
|
|
@@ -339,7 +302,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
339
302
|
|
|
340
303
|
this.logger?.debug('Decrypting data with AES-GCM using QuickCrypto', {
|
|
341
304
|
dataSize: data.byteLength,
|
|
342
|
-
ivSize: iv.byteLength
|
|
305
|
+
ivSize: iv.byteLength,
|
|
343
306
|
});
|
|
344
307
|
|
|
345
308
|
const secretKey = key as QuickCryptoKey;
|
|
@@ -347,7 +310,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
347
310
|
if (secretKey.type !== 'secret') {
|
|
348
311
|
const error = new Error('Expected secret key');
|
|
349
312
|
this.logger?.error('Decryption failed - invalid key type', {
|
|
350
|
-
actualType: secretKey.type
|
|
313
|
+
actualType: secretKey.type,
|
|
351
314
|
});
|
|
352
315
|
throw error;
|
|
353
316
|
}
|
|
@@ -378,7 +341,7 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
378
341
|
result.set(final, decrypted.length);
|
|
379
342
|
|
|
380
343
|
this.logger?.debug('Decryption completed using QuickCrypto', {
|
|
381
|
-
plaintextSize: result.byteLength
|
|
344
|
+
plaintextSize: result.byteLength,
|
|
382
345
|
});
|
|
383
346
|
|
|
384
347
|
return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
|
|
@@ -386,8 +349,8 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
386
349
|
this.logger?.error('QuickCrypto decryption failed, falling back to Noble', {
|
|
387
350
|
error: {
|
|
388
351
|
message: error instanceof Error ? error.message : String(error),
|
|
389
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
390
|
-
}
|
|
352
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
353
|
+
},
|
|
391
354
|
});
|
|
392
355
|
return this.nobleProvider.decrypt(key, data, iv);
|
|
393
356
|
}
|
|
@@ -417,8 +380,8 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
417
380
|
} catch (error) {
|
|
418
381
|
this.logger?.error('QuickCrypto HMAC failed, falling back to Noble', {
|
|
419
382
|
error: {
|
|
420
|
-
message: error instanceof Error ? error.message : String(error)
|
|
421
|
-
}
|
|
383
|
+
message: error instanceof Error ? error.message : String(error),
|
|
384
|
+
},
|
|
422
385
|
});
|
|
423
386
|
return this.nobleProvider.generateHMAC(key, data);
|
|
424
387
|
}
|
|
@@ -452,32 +415,10 @@ export class QuickCryptoProvider implements CryptoProvider {
|
|
|
452
415
|
} catch (error) {
|
|
453
416
|
this.logger?.error('QuickCrypto HMAC verification failed, falling back to Noble', {
|
|
454
417
|
error: {
|
|
455
|
-
message: error instanceof Error ? error.message : String(error)
|
|
456
|
-
}
|
|
418
|
+
message: error instanceof Error ? error.message : String(error),
|
|
419
|
+
},
|
|
457
420
|
});
|
|
458
421
|
return this.nobleProvider.verifyHMAC(key, data, mac);
|
|
459
422
|
}
|
|
460
423
|
}
|
|
461
424
|
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Self-register QuickCrypto provider on import
|
|
465
|
-
* This allows the provider to be available when explicitly imported
|
|
466
|
-
*
|
|
467
|
-
* Note: QuickCrypto depends on Noble for ECDH operations, so we auto-register
|
|
468
|
-
* Noble as well to ensure all required functionality is available.
|
|
469
|
-
*/
|
|
470
|
-
// Auto-register noble as a dependency (QuickCrypto delegates ECDH to Noble)
|
|
471
|
-
import './noble-provider';
|
|
472
|
-
|
|
473
|
-
registerCryptoProvider('quickcrypto', (logger) => new QuickCryptoProvider(logger));
|
|
474
|
-
|
|
475
|
-
// TypeScript module augmentation to track this provider is available
|
|
476
|
-
declare global {
|
|
477
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
478
|
-
namespace BananaLink {
|
|
479
|
-
interface RegisteredCryptoProviders {
|
|
480
|
-
quickcrypto: true;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Global registry for crypto providers
|
|
3
3
|
*
|
|
4
|
-
* This
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* @deprecated This module is deprecated. Use crypto factory functions instead:
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { BananaLink, webSocketTransport, nobleCrypto } from '@bananalink-sdk/client'
|
|
7
|
+
*
|
|
8
|
+
* const client = new BananaLink({
|
|
9
|
+
* transport: webSocketTransport(),
|
|
10
|
+
* crypto: nobleCrypto(),
|
|
11
|
+
* })
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The registry is kept for backward compatibility but will be removed in a future version.
|
|
7
15
|
*/
|
|
8
16
|
|
|
9
17
|
import type { CryptoProvider, CryptoProviderType } from '../../types/crypto-provider';
|
|
@@ -35,10 +43,7 @@ const cryptoProviders = new Map<CryptoProviderType, CryptoProviderFactory>();
|
|
|
35
43
|
* registerCryptoProvider('noble', (logger) => new NobleCryptoProvider(logger));
|
|
36
44
|
* ```
|
|
37
45
|
*/
|
|
38
|
-
export function registerCryptoProvider(
|
|
39
|
-
type: CryptoProviderType,
|
|
40
|
-
factory: CryptoProviderFactory
|
|
41
|
-
): void {
|
|
46
|
+
export function registerCryptoProvider(type: CryptoProviderType, factory: CryptoProviderFactory): void {
|
|
42
47
|
// Skip if already registered (idempotent)
|
|
43
48
|
if (cryptoProviders.has(type)) {
|
|
44
49
|
return;
|
|
@@ -62,8 +67,8 @@ export function getCryptoProviderFactory(type: CryptoProviderType): CryptoProvid
|
|
|
62
67
|
|
|
63
68
|
throw new Error(
|
|
64
69
|
`Crypto provider '${type}' is not registered. ${importHint}\n` +
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
`Available providers: ${availableProviders || 'none'}\n` +
|
|
71
|
+
`Make sure to import the provider before using it.`,
|
|
67
72
|
);
|
|
68
73
|
}
|
|
69
74
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Logger } from '@bananalink-sdk/logger';
|
|
2
2
|
import type { CryptoProvider, CryptoKeyLike, ProviderKeyPair } from '../../types/crypto-provider';
|
|
3
|
-
import { registerCryptoProvider } from './registry';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* WebCrypto CryptoKey wrapper to implement CryptoKeyLike interface
|
|
@@ -74,7 +73,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
74
73
|
namedCurve: 'P-256',
|
|
75
74
|
},
|
|
76
75
|
true, // extractable
|
|
77
|
-
['deriveKey']
|
|
76
|
+
['deriveKey'],
|
|
78
77
|
);
|
|
79
78
|
|
|
80
79
|
this.logger?.debug('Key pair generation completed');
|
|
@@ -110,7 +109,9 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
110
109
|
|
|
111
110
|
this.logger?.debug('importPublicKey called', {
|
|
112
111
|
keyLength: keyBytes.length,
|
|
113
|
-
keyBytesFirst20: Array.from(keyBytes.slice(0, 20))
|
|
112
|
+
keyBytesFirst20: Array.from(keyBytes.slice(0, 20))
|
|
113
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
114
|
+
.join(' '),
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
try {
|
|
@@ -122,7 +123,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
122
123
|
namedCurve: 'P-256',
|
|
123
124
|
},
|
|
124
125
|
true,
|
|
125
|
-
[]
|
|
126
|
+
[],
|
|
126
127
|
);
|
|
127
128
|
|
|
128
129
|
this.logger?.debug('Public key import successful');
|
|
@@ -131,8 +132,8 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
131
132
|
this.logger?.error('Public key import failed', {
|
|
132
133
|
error: {
|
|
133
134
|
message: error instanceof Error ? error.message : String(error),
|
|
134
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
135
|
-
}
|
|
135
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
136
|
+
},
|
|
136
137
|
});
|
|
137
138
|
throw new Error(`Invalid P-256 public key: ${String(error)}`);
|
|
138
139
|
}
|
|
@@ -143,7 +144,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
143
144
|
*/
|
|
144
145
|
async importPrivateKey(keyData: ArrayBuffer): Promise<CryptoKeyLike> {
|
|
145
146
|
this.logger?.debug('Importing private key', {
|
|
146
|
-
keyLength: keyData.byteLength
|
|
147
|
+
keyLength: keyData.byteLength,
|
|
147
148
|
});
|
|
148
149
|
|
|
149
150
|
try {
|
|
@@ -155,7 +156,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
155
156
|
namedCurve: 'P-256',
|
|
156
157
|
},
|
|
157
158
|
true,
|
|
158
|
-
['deriveKey']
|
|
159
|
+
['deriveKey'],
|
|
159
160
|
);
|
|
160
161
|
|
|
161
162
|
this.logger?.debug('Private key import successful');
|
|
@@ -164,8 +165,8 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
164
165
|
this.logger?.error('Private key import failed', {
|
|
165
166
|
error: {
|
|
166
167
|
message: error instanceof Error ? error.message : String(error),
|
|
167
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
168
|
-
}
|
|
168
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
169
|
+
},
|
|
169
170
|
});
|
|
170
171
|
throw new Error(`Invalid P-256 private key: ${String(error)}`);
|
|
171
172
|
}
|
|
@@ -189,7 +190,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
189
190
|
length: 256,
|
|
190
191
|
},
|
|
191
192
|
true,
|
|
192
|
-
['encrypt', 'decrypt']
|
|
193
|
+
['encrypt', 'decrypt'],
|
|
193
194
|
);
|
|
194
195
|
|
|
195
196
|
this.logger?.debug('Shared secret derivation completed');
|
|
@@ -198,8 +199,8 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
198
199
|
this.logger?.error('Shared secret derivation failed', {
|
|
199
200
|
error: {
|
|
200
201
|
message: error instanceof Error ? error.message : String(error),
|
|
201
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
202
|
-
}
|
|
202
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
203
|
+
},
|
|
203
204
|
});
|
|
204
205
|
throw error;
|
|
205
206
|
}
|
|
@@ -215,7 +216,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
215
216
|
await crypto.subtle.exportKey('raw', unwrapCryptoKey(sharedSecret)),
|
|
216
217
|
{ name: 'HKDF' },
|
|
217
218
|
false,
|
|
218
|
-
['deriveKey']
|
|
219
|
+
['deriveKey'],
|
|
219
220
|
);
|
|
220
221
|
|
|
221
222
|
// Derive encryption key using HKDF
|
|
@@ -229,7 +230,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
229
230
|
baseKey,
|
|
230
231
|
{ name: 'AES-GCM', length: 256 },
|
|
231
232
|
true,
|
|
232
|
-
['encrypt', 'decrypt']
|
|
233
|
+
['encrypt', 'decrypt'],
|
|
233
234
|
);
|
|
234
235
|
return new WebCryptoKeyWrapper(derivedKey);
|
|
235
236
|
}
|
|
@@ -250,7 +251,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
250
251
|
async encrypt(key: CryptoKeyLike, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer> {
|
|
251
252
|
this.logger?.debug('Encrypting data with AES-GCM', {
|
|
252
253
|
dataSize: data.byteLength,
|
|
253
|
-
ivSize: iv.byteLength
|
|
254
|
+
ivSize: iv.byteLength,
|
|
254
255
|
});
|
|
255
256
|
|
|
256
257
|
try {
|
|
@@ -261,19 +262,19 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
261
262
|
tagLength: 128, // 128-bit tag
|
|
262
263
|
},
|
|
263
264
|
unwrapCryptoKey(key),
|
|
264
|
-
data
|
|
265
|
+
data,
|
|
265
266
|
);
|
|
266
267
|
|
|
267
268
|
this.logger?.debug('Encryption completed', {
|
|
268
|
-
ciphertextSize: ciphertext.byteLength
|
|
269
|
+
ciphertextSize: ciphertext.byteLength,
|
|
269
270
|
});
|
|
270
271
|
return ciphertext;
|
|
271
272
|
} catch (error) {
|
|
272
273
|
this.logger?.error('Encryption failed', {
|
|
273
274
|
error: {
|
|
274
275
|
message: error instanceof Error ? error.message : String(error),
|
|
275
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
276
|
-
}
|
|
276
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
277
|
+
},
|
|
277
278
|
});
|
|
278
279
|
throw error;
|
|
279
280
|
}
|
|
@@ -285,7 +286,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
285
286
|
async decrypt(key: CryptoKeyLike, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer> {
|
|
286
287
|
this.logger?.debug('Decrypting data with AES-GCM', {
|
|
287
288
|
dataSize: data.byteLength,
|
|
288
|
-
ivSize: iv.byteLength
|
|
289
|
+
ivSize: iv.byteLength,
|
|
289
290
|
});
|
|
290
291
|
|
|
291
292
|
try {
|
|
@@ -296,19 +297,19 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
296
297
|
tagLength: 128, // 128-bit tag
|
|
297
298
|
},
|
|
298
299
|
unwrapCryptoKey(key),
|
|
299
|
-
data
|
|
300
|
+
data,
|
|
300
301
|
);
|
|
301
302
|
|
|
302
303
|
this.logger?.debug('Decryption completed', {
|
|
303
|
-
plaintextSize: plaintext.byteLength
|
|
304
|
+
plaintextSize: plaintext.byteLength,
|
|
304
305
|
});
|
|
305
306
|
return plaintext;
|
|
306
307
|
} catch (error) {
|
|
307
308
|
this.logger?.error('Decryption failed', {
|
|
308
309
|
error: {
|
|
309
310
|
message: error instanceof Error ? error.message : String(error),
|
|
310
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
311
|
-
}
|
|
311
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
312
|
+
},
|
|
312
313
|
});
|
|
313
314
|
throw error;
|
|
314
315
|
}
|
|
@@ -324,7 +325,7 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
324
325
|
await crypto.subtle.exportKey('raw', unwrapCryptoKey(key)),
|
|
325
326
|
{ name: 'HMAC', hash: 'SHA-256' },
|
|
326
327
|
false,
|
|
327
|
-
['sign']
|
|
328
|
+
['sign'],
|
|
328
329
|
);
|
|
329
330
|
|
|
330
331
|
return crypto.subtle.sign('HMAC', hmacKey, data);
|
|
@@ -340,25 +341,9 @@ export class WebCryptoProvider implements CryptoProvider {
|
|
|
340
341
|
await crypto.subtle.exportKey('raw', unwrapCryptoKey(key)),
|
|
341
342
|
{ name: 'HMAC', hash: 'SHA-256' },
|
|
342
343
|
false,
|
|
343
|
-
['verify']
|
|
344
|
+
['verify'],
|
|
344
345
|
);
|
|
345
346
|
|
|
346
347
|
return crypto.subtle.verify('HMAC', hmacKey, mac, data);
|
|
347
348
|
}
|
|
348
349
|
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Self-register WebCrypto provider on import
|
|
352
|
-
* This allows the provider to be available when explicitly imported
|
|
353
|
-
*/
|
|
354
|
-
registerCryptoProvider('webcrypto', () => new WebCryptoProvider());
|
|
355
|
-
|
|
356
|
-
// TypeScript module augmentation to track this provider is available
|
|
357
|
-
declare global {
|
|
358
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
359
|
-
namespace BananaLink {
|
|
360
|
-
interface RegisteredCryptoProviders {
|
|
361
|
-
webcrypto: true;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from './types/providers';
|
|
|
17
17
|
export * from './types/wallet-messages';
|
|
18
18
|
export * from './types/client-messages';
|
|
19
19
|
export * from './types/relay-messages';
|
|
20
|
+
export * from './types/persistence';
|
|
20
21
|
|
|
21
22
|
// Note: Zod validation schemas have been moved to '@bananalink-sdk/protocol/schemas'
|
|
22
23
|
// For runtime validation, import from '@bananalink-sdk/protocol/schemas'
|
|
@@ -15,6 +15,20 @@ export const clientSessionClaimSchema = z.object({
|
|
|
15
15
|
// Client message payload schemas
|
|
16
16
|
export const clientReconnectPayloadSchema = z.object({
|
|
17
17
|
type: z.literal('client_reconnect'),
|
|
18
|
+
sessionClaim: clientSessionClaimSchema,
|
|
19
|
+
clientPublicKey: z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1, 'Client public key cannot be empty')
|
|
22
|
+
.regex(/^(AES-GCM:|cleartext:)/, 'Client public key must start with "AES-GCM:" or "cleartext:"'),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const clientHandshakeMessageSchema = z.object({
|
|
26
|
+
type: z.literal('client_handshake'),
|
|
27
|
+
clientPublicKey: z
|
|
28
|
+
.string()
|
|
29
|
+
.min(1, 'Client public key cannot be empty')
|
|
30
|
+
.regex(/^(AES-GCM:|cleartext:)/, 'Client public key must start with "AES-GCM:" or "cleartext:"'),
|
|
31
|
+
timestamp: z.number().int().positive('Timestamp must be a positive integer'),
|
|
18
32
|
});
|
|
19
33
|
|
|
20
34
|
export const closeSessionPayloadSchema = z.object({
|
|
@@ -46,6 +46,10 @@ export const authenticateConnectionPayloadSchema = z.object({
|
|
|
46
46
|
|
|
47
47
|
export const walletReconnectPayloadSchema = z.object({
|
|
48
48
|
type: z.literal('wallet_reconnect'),
|
|
49
|
+
walletPublicKey: z
|
|
50
|
+
.string()
|
|
51
|
+
.min(1, 'Wallet public key cannot be empty')
|
|
52
|
+
.regex(/^(AES-GCM:|cleartext:)/, 'Wallet public key must start with "AES-GCM:" or "cleartext:"'),
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
// Union of all wallet message payloads
|
|
@@ -29,10 +29,26 @@ export interface ClientSessionClaim {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Client reconnect payload
|
|
32
|
-
* Used to reconnect after network interruption
|
|
32
|
+
* Used to reconnect after network interruption with re-handshake support
|
|
33
33
|
*/
|
|
34
34
|
export interface ClientReconnectPayload {
|
|
35
35
|
type: 'client_reconnect';
|
|
36
|
+
/** Client session claim for authentication */
|
|
37
|
+
sessionClaim: ClientSessionClaim;
|
|
38
|
+
/** Client's public key for ECDH key exchange in format "algorithm:base64_public_key" (e.g., "AES-GCM:...") */
|
|
39
|
+
clientPublicKey: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Client handshake message
|
|
44
|
+
* Sent during re-handshake when client is the connected party responding to wallet reconnect
|
|
45
|
+
*/
|
|
46
|
+
export interface ClientHandshakeMessage {
|
|
47
|
+
type: 'client_handshake';
|
|
48
|
+
/** Client's public key for ECDH key exchange in format "algorithm:base64_public_key" (e.g., "AES-GCM:...") */
|
|
49
|
+
clientPublicKey: string;
|
|
50
|
+
/** Message timestamp */
|
|
51
|
+
timestamp: number;
|
|
36
52
|
}
|
|
37
53
|
|
|
38
54
|
/**
|
|
@@ -52,6 +68,8 @@ export type ClientMessagePayload =
|
|
|
52
68
|
// Session management payloads
|
|
53
69
|
| ClientReconnectPayload
|
|
54
70
|
| CloseSessionPayload
|
|
71
|
+
// Re-handshake payload (sent by connected client when wallet reconnects)
|
|
72
|
+
| ClientHandshakeMessage
|
|
55
73
|
// Post-authentication operation request payloads (v2.1+)
|
|
56
74
|
| SignMessageRequestPayload
|
|
57
75
|
| SignTypedDataRequestPayload
|
|
@@ -82,3 +100,10 @@ export function isClientReconnectPayload(payload: ClientMessagePayload): payload
|
|
|
82
100
|
export function isCloseSessionPayload(payload: ClientMessagePayload): payload is CloseSessionPayload {
|
|
83
101
|
return payload?.type === 'close_session';
|
|
84
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Type guard for client handshake message (used during re-handshake)
|
|
106
|
+
*/
|
|
107
|
+
export function isClientHandshakeMessage(message: unknown): message is ClientHandshakeMessage {
|
|
108
|
+
return (message as ClientHandshakeMessage)?.type === 'client_handshake';
|
|
109
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export type {
|
|
|
32
32
|
ConnectionRejectedMessage,
|
|
33
33
|
ConnectionAuthenticatedMessage,
|
|
34
34
|
RelayToDAppMessage,
|
|
35
|
+
RelayToWalletMessage,
|
|
35
36
|
PrefetchMetadataResponse,
|
|
36
37
|
} from './wallet-messages';
|
|
37
38
|
|
|
@@ -115,12 +116,14 @@ export type {
|
|
|
115
116
|
ClientReconnectPayload,
|
|
116
117
|
CloseSessionPayload,
|
|
117
118
|
ClientMessagePayload,
|
|
119
|
+
ClientHandshakeMessage,
|
|
118
120
|
} from './client-messages';
|
|
119
121
|
|
|
120
122
|
// Re-export client message type guards
|
|
121
123
|
export {
|
|
122
124
|
isClientReconnectPayload,
|
|
123
125
|
isCloseSessionPayload,
|
|
126
|
+
isClientHandshakeMessage,
|
|
124
127
|
} from './client-messages';
|
|
125
128
|
|
|
126
129
|
// Re-export v2.0 relay notification message types
|
|
@@ -195,3 +198,9 @@ export type {
|
|
|
195
198
|
KeyDerivation,
|
|
196
199
|
CryptoPayload,
|
|
197
200
|
} from './crypto';
|
|
201
|
+
|
|
202
|
+
export type {
|
|
203
|
+
PersistedClientSession,
|
|
204
|
+
PersistedWalletSession,
|
|
205
|
+
SessionStorage,
|
|
206
|
+
} from './persistence';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ClientSessionClaim } from './client-messages';
|
|
2
|
+
import type { WalletSessionClaim } from './wallet-messages';
|
|
3
|
+
import type { DAppMetadata } from './core';
|
|
4
|
+
|
|
5
|
+
export interface PersistedClientSession {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
clientSessionClaim: ClientSessionClaim;
|
|
8
|
+
peerPublicKey: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
lastConnectedAt: number;
|
|
11
|
+
dAppMetadata?: {
|
|
12
|
+
name: string;
|
|
13
|
+
url: string;
|
|
14
|
+
icon?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PersistedWalletSession {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
walletSessionClaim: WalletSessionClaim;
|
|
21
|
+
peerPublicKey: string;
|
|
22
|
+
createdAt: number;
|
|
23
|
+
lastConnectedAt: number;
|
|
24
|
+
dAppMetadata?: DAppMetadata;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SessionStorage {
|
|
28
|
+
get(key: string): Promise<string | null>;
|
|
29
|
+
set(key: string, value: string): Promise<void>;
|
|
30
|
+
remove(key: string): Promise<void>;
|
|
31
|
+
getAllKeys(): Promise<string[]>;
|
|
32
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { EncryptedPayload } from './discovery';
|
|
7
7
|
import type { DAppMetadata, SessionConfig } from './core';
|
|
8
|
-
import type { CloseSessionPayload } from './client-messages';
|
|
8
|
+
import type { CloseSessionPayload, ClientHandshakeMessage } from './client-messages';
|
|
9
9
|
import type {
|
|
10
10
|
RequestFulfilledPayload,
|
|
11
11
|
RequestRejectedPayload,
|
|
@@ -82,10 +82,12 @@ export interface AuthenticateConnectionPayload {
|
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Wallet reconnect payload
|
|
85
|
-
* Used to reconnect after network interruption
|
|
85
|
+
* Used to reconnect after network interruption with re-handshake support
|
|
86
86
|
*/
|
|
87
87
|
export interface WalletReconnectPayload {
|
|
88
88
|
type: 'wallet_reconnect';
|
|
89
|
+
/** Wallet's public key for ECDH key exchange in format "algorithm:base64_public_key" (e.g., "AES-GCM:...") */
|
|
90
|
+
walletPublicKey: string;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
/**
|
|
@@ -212,6 +214,8 @@ export type RelayToDAppMessage =
|
|
|
212
214
|
| ConnectionRejectedMessage
|
|
213
215
|
| ConnectionAuthenticatedMessage;
|
|
214
216
|
|
|
217
|
+
export type RelayToWalletMessage = ClientHandshakeMessage;
|
|
218
|
+
|
|
215
219
|
/**
|
|
216
220
|
* Type guards for wallet messages
|
|
217
221
|
*/
|