@bananalink-sdk/protocol 1.2.7

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.
Files changed (158) hide show
  1. package/README.md +604 -0
  2. package/dist/chunk-32OWUOZ3.js +308 -0
  3. package/dist/chunk-32OWUOZ3.js.map +1 -0
  4. package/dist/chunk-65HNHRJK.cjs +123 -0
  5. package/dist/chunk-65HNHRJK.cjs.map +1 -0
  6. package/dist/chunk-7KYDLL3B.js +480 -0
  7. package/dist/chunk-7KYDLL3B.js.map +1 -0
  8. package/dist/chunk-A6FLEJ7R.cjs +62 -0
  9. package/dist/chunk-A6FLEJ7R.cjs.map +1 -0
  10. package/dist/chunk-CUJK7ZTS.js +217 -0
  11. package/dist/chunk-CUJK7ZTS.js.map +1 -0
  12. package/dist/chunk-GI3BUPIH.cjs +236 -0
  13. package/dist/chunk-GI3BUPIH.cjs.map +1 -0
  14. package/dist/chunk-JXHV66Q4.js +106 -0
  15. package/dist/chunk-JXHV66Q4.js.map +1 -0
  16. package/dist/chunk-KNGZKGRS.cjs +552 -0
  17. package/dist/chunk-KNGZKGRS.cjs.map +1 -0
  18. package/dist/chunk-LELPCIE7.js +840 -0
  19. package/dist/chunk-LELPCIE7.js.map +1 -0
  20. package/dist/chunk-MCZG7QEM.cjs +310 -0
  21. package/dist/chunk-MCZG7QEM.cjs.map +1 -0
  22. package/dist/chunk-TCVKC227.js +56 -0
  23. package/dist/chunk-TCVKC227.js.map +1 -0
  24. package/dist/chunk-VXLUSU5B.cjs +856 -0
  25. package/dist/chunk-VXLUSU5B.cjs.map +1 -0
  26. package/dist/chunk-WCQVDF3K.js +12 -0
  27. package/dist/chunk-WCQVDF3K.js.map +1 -0
  28. package/dist/chunk-WGEGR3DF.cjs +15 -0
  29. package/dist/chunk-WGEGR3DF.cjs.map +1 -0
  30. package/dist/client-session-claim-3QF3noOr.d.ts +197 -0
  31. package/dist/client-session-claim-C4lUik3b.d.cts +197 -0
  32. package/dist/core-DMhuNfoz.d.cts +62 -0
  33. package/dist/core-DMhuNfoz.d.ts +62 -0
  34. package/dist/crypto/providers/noble-provider.cjs +14 -0
  35. package/dist/crypto/providers/noble-provider.cjs.map +1 -0
  36. package/dist/crypto/providers/noble-provider.d.cts +30 -0
  37. package/dist/crypto/providers/noble-provider.d.ts +30 -0
  38. package/dist/crypto/providers/noble-provider.js +5 -0
  39. package/dist/crypto/providers/noble-provider.js.map +1 -0
  40. package/dist/crypto/providers/node-provider.cjs +308 -0
  41. package/dist/crypto/providers/node-provider.cjs.map +1 -0
  42. package/dist/crypto/providers/node-provider.d.cts +32 -0
  43. package/dist/crypto/providers/node-provider.d.ts +32 -0
  44. package/dist/crypto/providers/node-provider.js +306 -0
  45. package/dist/crypto/providers/node-provider.js.map +1 -0
  46. package/dist/crypto/providers/quickcrypto-provider.cjs +339 -0
  47. package/dist/crypto/providers/quickcrypto-provider.cjs.map +1 -0
  48. package/dist/crypto/providers/quickcrypto-provider.d.cts +34 -0
  49. package/dist/crypto/providers/quickcrypto-provider.d.ts +34 -0
  50. package/dist/crypto/providers/quickcrypto-provider.js +337 -0
  51. package/dist/crypto/providers/quickcrypto-provider.js.map +1 -0
  52. package/dist/crypto/providers/webcrypto-provider.cjs +310 -0
  53. package/dist/crypto/providers/webcrypto-provider.cjs.map +1 -0
  54. package/dist/crypto/providers/webcrypto-provider.d.cts +30 -0
  55. package/dist/crypto/providers/webcrypto-provider.d.ts +30 -0
  56. package/dist/crypto/providers/webcrypto-provider.js +308 -0
  57. package/dist/crypto/providers/webcrypto-provider.js.map +1 -0
  58. package/dist/crypto-BUS06Qz-.d.cts +40 -0
  59. package/dist/crypto-BUS06Qz-.d.ts +40 -0
  60. package/dist/crypto-export.cjs +790 -0
  61. package/dist/crypto-export.cjs.map +1 -0
  62. package/dist/crypto-export.d.cts +257 -0
  63. package/dist/crypto-export.d.ts +257 -0
  64. package/dist/crypto-export.js +709 -0
  65. package/dist/crypto-export.js.map +1 -0
  66. package/dist/crypto-provider-deYoVIxi.d.cts +36 -0
  67. package/dist/crypto-provider-deYoVIxi.d.ts +36 -0
  68. package/dist/index.cjs +615 -0
  69. package/dist/index.cjs.map +1 -0
  70. package/dist/index.d.cts +379 -0
  71. package/dist/index.d.ts +379 -0
  72. package/dist/index.js +504 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/schemas-export.cjs +294 -0
  75. package/dist/schemas-export.cjs.map +1 -0
  76. package/dist/schemas-export.d.cts +1598 -0
  77. package/dist/schemas-export.d.ts +1598 -0
  78. package/dist/schemas-export.js +5 -0
  79. package/dist/schemas-export.js.map +1 -0
  80. package/dist/siwe-export.cjs +237 -0
  81. package/dist/siwe-export.cjs.map +1 -0
  82. package/dist/siwe-export.d.cts +27 -0
  83. package/dist/siwe-export.d.ts +27 -0
  84. package/dist/siwe-export.js +228 -0
  85. package/dist/siwe-export.js.map +1 -0
  86. package/dist/testing.cjs +54 -0
  87. package/dist/testing.cjs.map +1 -0
  88. package/dist/testing.d.cts +20 -0
  89. package/dist/testing.d.ts +20 -0
  90. package/dist/testing.js +51 -0
  91. package/dist/testing.js.map +1 -0
  92. package/dist/validation-export.cjs +359 -0
  93. package/dist/validation-export.cjs.map +1 -0
  94. package/dist/validation-export.d.cts +3 -0
  95. package/dist/validation-export.d.ts +3 -0
  96. package/dist/validation-export.js +6 -0
  97. package/dist/validation-export.js.map +1 -0
  98. package/dist/validators-export.cjs +73 -0
  99. package/dist/validators-export.cjs.map +1 -0
  100. package/dist/validators-export.d.cts +37 -0
  101. package/dist/validators-export.d.ts +37 -0
  102. package/dist/validators-export.js +4 -0
  103. package/dist/validators-export.js.map +1 -0
  104. package/package.json +140 -0
  105. package/src/constants/index.ts +205 -0
  106. package/src/crypto/context.ts +228 -0
  107. package/src/crypto/diagnostics.ts +772 -0
  108. package/src/crypto/errors.ts +114 -0
  109. package/src/crypto/index.ts +89 -0
  110. package/src/crypto/payload-handler.ts +102 -0
  111. package/src/crypto/providers/compliance-provider.ts +579 -0
  112. package/src/crypto/providers/factory.ts +204 -0
  113. package/src/crypto/providers/index.ts +44 -0
  114. package/src/crypto/providers/noble-provider.ts +392 -0
  115. package/src/crypto/providers/node-provider.ts +433 -0
  116. package/src/crypto/providers/quickcrypto-provider.ts +483 -0
  117. package/src/crypto/providers/registry.ts +129 -0
  118. package/src/crypto/providers/webcrypto-provider.ts +364 -0
  119. package/src/crypto/session-security.ts +185 -0
  120. package/src/crypto/types.ts +93 -0
  121. package/src/crypto/utils.ts +190 -0
  122. package/src/crypto-export.ts +21 -0
  123. package/src/index.ts +38 -0
  124. package/src/schemas/auth.ts +60 -0
  125. package/src/schemas/client-messages.ts +57 -0
  126. package/src/schemas/core.ts +144 -0
  127. package/src/schemas/crypto.ts +65 -0
  128. package/src/schemas/discovery.ts +79 -0
  129. package/src/schemas/index.ts +239 -0
  130. package/src/schemas/relay-messages.ts +45 -0
  131. package/src/schemas/wallet-messages.ts +177 -0
  132. package/src/schemas-export.ts +23 -0
  133. package/src/siwe-export.ts +27 -0
  134. package/src/testing.ts +71 -0
  135. package/src/types/auth.ts +60 -0
  136. package/src/types/client-messages.ts +84 -0
  137. package/src/types/core.ts +131 -0
  138. package/src/types/crypto-provider.ts +264 -0
  139. package/src/types/crypto.ts +90 -0
  140. package/src/types/discovery.ts +50 -0
  141. package/src/types/errors.ts +87 -0
  142. package/src/types/index.ts +197 -0
  143. package/src/types/post-auth-operations.ts +363 -0
  144. package/src/types/providers.ts +72 -0
  145. package/src/types/relay-messages.ts +60 -0
  146. package/src/types/request-lifecycle.ts +161 -0
  147. package/src/types/signing-operations.ts +99 -0
  148. package/src/types/wallet-messages.ts +251 -0
  149. package/src/utils/client-session-claim.ts +188 -0
  150. package/src/utils/index.ts +54 -0
  151. package/src/utils/public-keys.ts +49 -0
  152. package/src/utils/siwe.ts +362 -0
  153. package/src/utils/url-decoding.ts +126 -0
  154. package/src/utils/url-encoding.ts +144 -0
  155. package/src/utils/wallet-session-claim.ts +188 -0
  156. package/src/validation-export.ts +32 -0
  157. package/src/validators/index.ts +222 -0
  158. package/src/validators-export.ts +8 -0
@@ -0,0 +1,433 @@
1
+ import type { Logger } from '@bananalink-sdk/logger';
2
+ import type { CryptoProvider, CryptoKeyLike, ProviderKeyPair } from '../../types/crypto-provider';
3
+ import { registerCryptoProvider } from './registry';
4
+
5
+ /**
6
+ * Type definition for Node.js crypto module (loaded dynamically to prevent Metro bundling)
7
+ */
8
+ type NodeCrypto = typeof import('crypto');
9
+
10
+ /**
11
+ * Node.js crypto.KeyObject wrapper to implement CryptoKeyLike interface
12
+ * Note: keyObject type is unknown at compile time (crypto.KeyObject | Buffer at runtime)
13
+ */
14
+ class NodeCryptoKeyWrapper implements CryptoKeyLike {
15
+ constructor(
16
+ private readonly keyObject: unknown, // crypto.KeyObject | Buffer at runtime
17
+ private readonly keyType: 'public' | 'private' | 'secret'
18
+ ) {}
19
+
20
+ get type(): 'public' | 'private' | 'secret' {
21
+ return this.keyType;
22
+ }
23
+
24
+ get algorithm(): string {
25
+ return 'ECDH-P256';
26
+ }
27
+
28
+ get extractable(): boolean {
29
+ return true;
30
+ }
31
+
32
+ get usages(): readonly string[] {
33
+ return this.keyType === 'secret' ? ['encrypt', 'decrypt'] : ['deriveKey'];
34
+ }
35
+
36
+ get nativeKey(): unknown {
37
+ return this.keyObject;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Helper function to unwrap CryptoKeyLike to native KeyObject or Buffer
43
+ */
44
+ function unwrapKeyObject(keyLike: CryptoKeyLike): unknown {
45
+ if (keyLike instanceof NodeCryptoKeyWrapper) {
46
+ return keyLike.nativeKey;
47
+ }
48
+ // Assume it's already a KeyObject
49
+ return keyLike;
50
+ }
51
+
52
+ /**
53
+ * Node.js crypto module implementation of CryptoProvider
54
+ * Optimized for backend services with native crypto performance
55
+ *
56
+ * This provider uses the native Node.js crypto module which provides:
57
+ * - Superior performance compared to Web Crypto API in Node.js
58
+ * - Direct access to OpenSSL optimizations
59
+ * - Zero additional dependencies
60
+ * - Full support for ECDH P-256 and AES-256-GCM
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { NodeCryptoProvider } from '@bananalink-sdk/protocol/crypto/provider/node';
65
+ * const provider = new NodeCryptoProvider();
66
+ * const keyPair = await provider.generateKeyPair();
67
+ * ```
68
+ */
69
+ export class NodeCryptoProvider implements CryptoProvider {
70
+ public readonly name = 'NodeCrypto';
71
+ private readonly logger?: Logger;
72
+ private cryptoModule: NodeCrypto | null = null;
73
+
74
+ public get isAvailable(): boolean {
75
+ try {
76
+ // Check if we're in Node.js environment
77
+ if (typeof process === 'undefined' || !process.versions?.node) {
78
+ return false;
79
+ }
80
+ // Crypto module availability will be checked when getCrypto() is called
81
+ return true;
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ constructor(logger?: Logger) {
88
+ if (!this.isAvailable) {
89
+ throw new Error('Node.js crypto module not available in this environment');
90
+ }
91
+ this.logger = logger?.child({ component: 'NodeCryptoProvider' });
92
+ }
93
+
94
+ /**
95
+ * Get crypto module instance via dynamic import
96
+ * This prevents Metro bundler from trying to resolve 'crypto' at build time
97
+ */
98
+ private async getCrypto(): Promise<NodeCrypto> {
99
+ if (!this.cryptoModule) {
100
+ try {
101
+ // Dynamic import prevents static analysis by bundlers
102
+ this.cryptoModule = await import('crypto');
103
+ } catch {
104
+ throw new Error(
105
+ 'Failed to load Node.js crypto module. ' +
106
+ 'This provider requires a Node.js environment.'
107
+ );
108
+ }
109
+ }
110
+ return this.cryptoModule;
111
+ }
112
+
113
+ /**
114
+ * Generate ECDH P-256 key pair using Node.js crypto
115
+ */
116
+ async generateKeyPair(): Promise<ProviderKeyPair> {
117
+ this.logger?.debug('Generating ECDH P-256 key pair with Node.js crypto');
118
+ const cryptoModule = await this.getCrypto();
119
+
120
+ return new Promise((resolve, reject) => {
121
+ cryptoModule.generateKeyPair(
122
+ 'ec',
123
+ {
124
+ namedCurve: 'prime256v1', // P-256
125
+ publicKeyEncoding: { type: 'spki', format: 'der' },
126
+ privateKeyEncoding: { type: 'pkcs8', format: 'der' },
127
+ },
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ (err: Error | null, publicKey: any, privateKey: any) => {
130
+ if (err) {
131
+ this.logger?.error('Key pair generation failed', { error: err });
132
+ reject(err);
133
+ return;
134
+ }
135
+
136
+ this.logger?.debug('Key pair generation completed');
137
+ resolve({
138
+ publicKey: new NodeCryptoKeyWrapper(publicKey, 'public'),
139
+ privateKey: new NodeCryptoKeyWrapper(privateKey, 'private'),
140
+ });
141
+ }
142
+ );
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Export public key to raw ArrayBuffer format (65 bytes uncompressed)
148
+ */
149
+ async exportPublicKey(publicKey: CryptoKeyLike): Promise<ArrayBuffer> {
150
+ this.logger?.debug('Exporting public key');
151
+ const cryptoModule = await this.getCrypto();
152
+ const keyObject = unwrapKeyObject(publicKey);
153
+
154
+ // If it's a Buffer (DER format from generateKeyPair), convert to KeyObject first
155
+ const keyObj = keyObject instanceof Buffer
156
+ ? cryptoModule.createPublicKey({ key: keyObject, format: 'der', type: 'spki' })
157
+ : keyObject;
158
+
159
+ // Export as JWK to get X/Y coordinates
160
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
161
+ const jwk = (keyObj as any).export({ format: 'jwk' });
162
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
163
+ const x = Buffer.from(jwk.x as string, 'base64url');
164
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
165
+ const y = Buffer.from(jwk.y as string, 'base64url');
166
+
167
+ // Construct 65-byte uncompressed point: 0x04 + X (32 bytes) + Y (32 bytes)
168
+ const uncompressed = Buffer.concat([Buffer.from([0x04]), x, y]);
169
+
170
+ return await Promise.resolve(
171
+ uncompressed.buffer.slice(
172
+ uncompressed.byteOffset,
173
+ uncompressed.byteOffset + uncompressed.byteLength
174
+ )
175
+ );
176
+ }
177
+
178
+ /**
179
+ * Export private key to raw ArrayBuffer format (32 bytes)
180
+ */
181
+ async exportPrivateKey(privateKey: CryptoKeyLike): Promise<ArrayBuffer> {
182
+ this.logger?.debug('Exporting private key');
183
+ const cryptoModule = await this.getCrypto();
184
+ const keyObject = unwrapKeyObject(privateKey);
185
+
186
+ // Convert KeyObject to raw format
187
+ const keyBuffer = keyObject instanceof Buffer ? keyObject : Buffer.from(keyObject as ArrayBuffer);
188
+ const keyObj = cryptoModule.createPrivateKey({
189
+ key: keyBuffer,
190
+ format: 'der',
191
+ type: 'pkcs8',
192
+ });
193
+
194
+ // Export as raw scalar (32 bytes)
195
+ const jwk = keyObj.export({ format: 'jwk' });
196
+ const dValue = Buffer.from(jwk.d as string, 'base64url');
197
+
198
+ return await Promise.resolve(
199
+ dValue.buffer.slice(dValue.byteOffset, dValue.byteOffset + dValue.byteLength)
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Import public key from raw ArrayBuffer format
205
+ */
206
+ async importPublicKey(keyData: ArrayBuffer): Promise<CryptoKeyLike> {
207
+ this.logger?.debug('Importing public key');
208
+ const cryptoModule = await this.getCrypto();
209
+ const keyBuffer = Buffer.from(keyData);
210
+
211
+ // Ensure it's uncompressed format (65 bytes starting with 0x04)
212
+ if (keyBuffer.length !== 65 || keyBuffer[0] !== 0x04) {
213
+ throw new Error('Invalid public key format: expected 65 bytes uncompressed point');
214
+ }
215
+
216
+ // Create KeyObject from raw uncompressed point
217
+ // We need to wrap it in DER format
218
+ const x = keyBuffer.slice(1, 33);
219
+ const y = keyBuffer.slice(33, 65);
220
+
221
+ // Create JWK representation
222
+ const jwk = {
223
+ kty: 'EC' as const,
224
+ crv: 'P-256' as const,
225
+ x: x.toString('base64url'),
226
+ y: y.toString('base64url'),
227
+ };
228
+
229
+ const keyObject = cryptoModule.createPublicKey({ key: jwk, format: 'jwk' });
230
+ return await Promise.resolve(new NodeCryptoKeyWrapper(keyObject, 'public'));
231
+ }
232
+
233
+ /**
234
+ * Import private key from raw ArrayBuffer format
235
+ */
236
+ async importPrivateKey(keyData: ArrayBuffer): Promise<CryptoKeyLike> {
237
+ this.logger?.debug('Importing private key');
238
+ const cryptoModule = await this.getCrypto();
239
+ const keyBuffer = Buffer.from(keyData);
240
+
241
+ if (keyBuffer.length !== 32) {
242
+ throw new Error('Invalid private key format: expected 32 bytes');
243
+ }
244
+
245
+ // Create JWK representation
246
+ const jwk = {
247
+ kty: 'EC' as const,
248
+ crv: 'P-256' as const,
249
+ d: keyBuffer.toString('base64url'),
250
+ };
251
+
252
+ const keyObject = cryptoModule.createPrivateKey({ key: jwk, format: 'jwk' });
253
+ return await Promise.resolve(new NodeCryptoKeyWrapper(keyObject, 'private'));
254
+ }
255
+
256
+ /**
257
+ * Derive shared secret using ECDH
258
+ */
259
+ async deriveSharedSecret(privateKey: CryptoKeyLike, publicKey: CryptoKeyLike): Promise<CryptoKeyLike> {
260
+ this.logger?.debug('Deriving shared secret');
261
+ const cryptoModule = await this.getCrypto();
262
+
263
+ // Create ECDH object
264
+ const ecdh = cryptoModule.createECDH('prime256v1');
265
+
266
+ // Set private key
267
+ const privateRaw = await this.exportPrivateKey(privateKey);
268
+ ecdh.setPrivateKey(Buffer.from(privateRaw));
269
+
270
+ // Compute shared secret
271
+ const publicRaw = await this.exportPublicKey(publicKey);
272
+ const sharedSecret = ecdh.computeSecret(Buffer.from(publicRaw));
273
+
274
+ // Wrap as secret key
275
+ const keyObject = cryptoModule.createSecretKey(sharedSecret);
276
+ return new NodeCryptoKeyWrapper(keyObject, 'secret');
277
+ }
278
+
279
+ /**
280
+ * Derive AES-GCM encryption key using HKDF-SHA256
281
+ */
282
+ async deriveEncryptionKey(
283
+ sharedSecret: CryptoKeyLike,
284
+ salt: ArrayBuffer,
285
+ info: ArrayBuffer
286
+ ): Promise<CryptoKeyLike> {
287
+ this.logger?.debug('Deriving AES-GCM encryption key');
288
+ const cryptoModule = await this.getCrypto();
289
+
290
+ // Extract raw shared secret
291
+ const keyObject = unwrapKeyObject(sharedSecret);
292
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
293
+ const sharedSecretRaw = (keyObject as any).export();
294
+
295
+ // Use HKDF to derive 32-byte key
296
+ const derivedKey = cryptoModule.hkdfSync(
297
+ 'sha256',
298
+ Buffer.isBuffer(sharedSecretRaw) ? sharedSecretRaw : Buffer.from(sharedSecretRaw),
299
+ Buffer.from(salt),
300
+ Buffer.from(info),
301
+ 32 // 256 bits
302
+ ) as Buffer;
303
+
304
+ // Create secret key object
305
+ const aesKeyObject = cryptoModule.createSecretKey(derivedKey);
306
+ return await Promise.resolve(new NodeCryptoKeyWrapper(aesKeyObject, 'secret'));
307
+ }
308
+
309
+ /**
310
+ * Encrypt data using AES-256-GCM
311
+ */
312
+ async encrypt(key: CryptoKeyLike, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer> {
313
+ this.logger?.debug('Encrypting with AES-256-GCM');
314
+ const cryptoModule = await this.getCrypto();
315
+ const keyObject = unwrapKeyObject(key);
316
+
317
+ const cipher = cryptoModule.createCipheriv(
318
+ 'aes-256-gcm',
319
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
320
+ (keyObject as any).export(),
321
+ Buffer.from(iv)
322
+ );
323
+
324
+ const encrypted = Buffer.concat([
325
+ cipher.update(Buffer.from(data)),
326
+ cipher.final(),
327
+ cipher.getAuthTag(),
328
+ ]);
329
+
330
+ return await Promise.resolve(
331
+ encrypted.buffer.slice(
332
+ encrypted.byteOffset,
333
+ encrypted.byteOffset + encrypted.byteLength
334
+ )
335
+ );
336
+ }
337
+
338
+ /**
339
+ * Decrypt data using AES-256-GCM
340
+ */
341
+ async decrypt(key: CryptoKeyLike, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer> {
342
+ this.logger?.debug('Decrypting with AES-256-GCM');
343
+ const cryptoModule = await this.getCrypto();
344
+ const keyObject = unwrapKeyObject(key);
345
+ const dataBuffer = Buffer.from(data);
346
+
347
+ // Last 16 bytes are the auth tag
348
+ const authTag = dataBuffer.slice(-16);
349
+ const ciphertext = dataBuffer.slice(0, -16);
350
+
351
+ const decipher = cryptoModule.createDecipheriv(
352
+ 'aes-256-gcm',
353
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
354
+ (keyObject as any).export(),
355
+ Buffer.from(iv)
356
+ );
357
+ decipher.setAuthTag(authTag);
358
+
359
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
360
+
361
+ return await Promise.resolve(
362
+ decrypted.buffer.slice(
363
+ decrypted.byteOffset,
364
+ decrypted.byteOffset + decrypted.byteLength
365
+ )
366
+ );
367
+ }
368
+
369
+ /**
370
+ * Generate HMAC-SHA256 authentication code
371
+ */
372
+ async generateHMAC(key: CryptoKeyLike, data: ArrayBuffer): Promise<ArrayBuffer> {
373
+ this.logger?.debug('Generating HMAC-SHA256');
374
+ const cryptoModule = await this.getCrypto();
375
+ const keyObject = unwrapKeyObject(key);
376
+
377
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
378
+ const hmac = cryptoModule.createHmac('sha256', (keyObject as any).export());
379
+ hmac.update(Buffer.from(data));
380
+ const mac = hmac.digest();
381
+
382
+ return await Promise.resolve(mac.buffer.slice(mac.byteOffset, mac.byteOffset + mac.byteLength));
383
+ }
384
+
385
+ /**
386
+ * Verify HMAC-SHA256 authentication code
387
+ */
388
+ async verifyHMAC(key: CryptoKeyLike, data: ArrayBuffer, mac: ArrayBuffer): Promise<boolean> {
389
+ this.logger?.debug('Verifying HMAC-SHA256');
390
+ const cryptoModule = await this.getCrypto();
391
+ const computed = await this.generateHMAC(key, data);
392
+ const expected = new Uint8Array(mac);
393
+ const actual = new Uint8Array(computed);
394
+
395
+ if (expected.length !== actual.length) {
396
+ return false;
397
+ }
398
+
399
+ // Constant-time comparison
400
+ return cryptoModule.timingSafeEqual(
401
+ Buffer.from(expected),
402
+ Buffer.from(actual)
403
+ );
404
+ }
405
+
406
+ /**
407
+ * Generate cryptographically secure random bytes
408
+ * Note: This is a synchronous method, so it uses require() instead of dynamic import
409
+ */
410
+ randomBytes(length: number): ArrayBuffer {
411
+ // Use cached module if available, otherwise use synchronous require
412
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
413
+ const cryptoModule = this.cryptoModule ?? require('crypto') as NodeCrypto;
414
+ const buffer = cryptoModule.randomBytes(length);
415
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Self-register Node provider on import
421
+ * This allows the provider to be available when explicitly imported
422
+ */
423
+ registerCryptoProvider('node', (logger) => new NodeCryptoProvider(logger));
424
+
425
+ // TypeScript module augmentation to track this provider is available
426
+ declare global {
427
+ // eslint-disable-next-line @typescript-eslint/no-namespace
428
+ namespace BananaLink {
429
+ interface RegisteredCryptoProviders {
430
+ node: true;
431
+ }
432
+ }
433
+ }