@bsv/sdk 1.9.2 → 1.9.4

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 (93) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/kvstore/GlobalKVStore.js +116 -98
  3. package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
  4. package/dist/cjs/src/kvstore/types.js.map +1 -1
  5. package/dist/cjs/src/overlay-tools/index.js +1 -0
  6. package/dist/cjs/src/overlay-tools/index.js.map +1 -1
  7. package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js +55 -0
  8. package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
  9. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  10. package/dist/esm/src/kvstore/GlobalKVStore.js +117 -99
  11. package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
  12. package/dist/esm/src/kvstore/types.js.map +1 -1
  13. package/dist/esm/src/overlay-tools/index.js +1 -0
  14. package/dist/esm/src/overlay-tools/index.js.map +1 -1
  15. package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js +48 -0
  16. package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
  17. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  18. package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
  19. package/dist/types/src/kvstore/types.d.ts +2 -0
  20. package/dist/types/src/kvstore/types.d.ts.map +1 -1
  21. package/dist/types/src/overlay-tools/index.d.ts +1 -0
  22. package/dist/types/src/overlay-tools/index.d.ts.map +1 -1
  23. package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts +14 -0
  24. package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts.map +1 -0
  25. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  26. package/dist/umd/bundle.js +2 -2
  27. package/dist/umd/bundle.js.map +1 -1
  28. package/docs/fast-docs.png +0 -0
  29. package/docs/index.md +49 -44
  30. package/docs/reference/kvstore.md +9 -0
  31. package/docs/reference/overlay-tools.md +32 -0
  32. package/docs/swagger.png +0 -0
  33. package/package.json +1 -1
  34. package/src/kvstore/GlobalKVStore.ts +134 -114
  35. package/src/kvstore/__tests/GlobalKVStore.test.ts +11 -1
  36. package/src/kvstore/types.ts +2 -0
  37. package/src/overlay-tools/index.ts +1 -0
  38. package/src/overlay-tools/withDoubleSpendRetry.ts +71 -0
  39. package/docs/MARKDOWN_VALIDATION_GUIDE.md +0 -175
  40. package/docs/concepts/beef.md +0 -92
  41. package/docs/concepts/chain-tracking.md +0 -134
  42. package/docs/concepts/decentralized-identity.md +0 -221
  43. package/docs/concepts/fees.md +0 -249
  44. package/docs/concepts/identity-certificates.md +0 -307
  45. package/docs/concepts/index.md +0 -77
  46. package/docs/concepts/key-management.md +0 -185
  47. package/docs/concepts/script-templates.md +0 -176
  48. package/docs/concepts/sdk-philosophy.md +0 -80
  49. package/docs/concepts/signatures.md +0 -194
  50. package/docs/concepts/spv-verification.md +0 -118
  51. package/docs/concepts/transaction-encoding.md +0 -167
  52. package/docs/concepts/transaction-structure.md +0 -67
  53. package/docs/concepts/trust-model.md +0 -139
  54. package/docs/concepts/verification.md +0 -250
  55. package/docs/concepts/wallet-integration.md +0 -101
  56. package/docs/guides/development-wallet-setup.md +0 -374
  57. package/docs/guides/direct-transaction-creation.md +0 -147
  58. package/docs/guides/http-client-configuration.md +0 -488
  59. package/docs/guides/index.md +0 -138
  60. package/docs/guides/large-transactions.md +0 -448
  61. package/docs/guides/multisig-transactions.md +0 -792
  62. package/docs/guides/security-best-practices.md +0 -494
  63. package/docs/guides/transaction-batching.md +0 -132
  64. package/docs/guides/transaction-signing-methods.md +0 -419
  65. package/docs/reference/arc-config.md +0 -698
  66. package/docs/reference/brc-100.md +0 -33
  67. package/docs/reference/configuration.md +0 -835
  68. package/docs/reference/debugging.md +0 -705
  69. package/docs/reference/errors.md +0 -597
  70. package/docs/reference/index.md +0 -111
  71. package/docs/reference/network-config.md +0 -914
  72. package/docs/reference/op-codes.md +0 -325
  73. package/docs/reference/transaction-signatures.md +0 -95
  74. package/docs/tutorials/advanced-transaction.md +0 -572
  75. package/docs/tutorials/aes-encryption.md +0 -949
  76. package/docs/tutorials/authfetch-tutorial.md +0 -986
  77. package/docs/tutorials/ecdh-key-exchange.md +0 -549
  78. package/docs/tutorials/elliptic-curve-fundamentals.md +0 -606
  79. package/docs/tutorials/error-handling.md +0 -1216
  80. package/docs/tutorials/first-transaction-low-level.md +0 -205
  81. package/docs/tutorials/first-transaction.md +0 -275
  82. package/docs/tutorials/hashes-and-hmacs.md +0 -788
  83. package/docs/tutorials/identity-management.md +0 -729
  84. package/docs/tutorials/index.md +0 -219
  85. package/docs/tutorials/key-management.md +0 -538
  86. package/docs/tutorials/protowallet-development.md +0 -743
  87. package/docs/tutorials/script-construction.md +0 -690
  88. package/docs/tutorials/spv-merkle-proofs.md +0 -685
  89. package/docs/tutorials/testnet-transactions-low-level.md +0 -359
  90. package/docs/tutorials/transaction-broadcasting.md +0 -538
  91. package/docs/tutorials/transaction-types.md +0 -420
  92. package/docs/tutorials/type-42.md +0 -568
  93. package/docs/tutorials/uhrp-storage.md +0 -599
@@ -1,949 +0,0 @@
1
- # AES Symmetric Encryption
2
-
3
- **Duration**: 60 minutes
4
- **Prerequisites**: Basic TypeScript knowledge, [First Transaction](./first-transaction.md) tutorial completed
5
-
6
- ## Learning Goals
7
-
8
- - Understand AES-GCM symmetric encryption principles
9
- - Use the `SymmetricKey` class for encryption and decryption
10
- - Implement secure key generation and management
11
- - Apply AES encryption in practical Bitcoin applications
12
- - Combine AES with ECDH for secure communication
13
- - Handle different data formats and encoding
14
-
15
- ## Introduction to AES Encryption
16
-
17
- Advanced Encryption Standard (AES) is a symmetric encryption algorithm where the same key is used for both encryption and decryption. The BSV TypeScript SDK provides the `SymmetricKey` class that implements AES-GCM (Galois/Counter Mode), which provides both confidentiality and authenticity.
18
-
19
- AES-GCM offers several advantages:
20
-
21
- - **Confidentiality**: Data is encrypted and unreadable without the key
22
- - **Authenticity**: Built-in authentication prevents tampering
23
- - **Performance**: Fast encryption/decryption operations
24
- - **Security**: Resistant to various cryptographic attacks
25
-
26
- ## Setting Up Your Environment
27
-
28
- First, let's import the necessary classes from the SDK:
29
-
30
- ```typescript
31
- import { SymmetricKey, Utils, Random } from '@bsv/sdk'
32
-
33
- // Helper function to convert hex string to byte array
34
- function hexToBytes(hex: string): number[] {
35
- const bytes = []
36
- for (let i = 0; i < hex.length; i += 2) {
37
- bytes.push(parseInt(hex.substr(i, 2), 16))
38
- }
39
- return bytes
40
- }
41
-
42
- // Helper function to convert byte array to hex string
43
- function bytesToHex(bytes: number[]): string {
44
- const hex = []
45
- for (const byte of bytes) {
46
- hex.push(byte.toString(16).padStart(2, '0'))
47
- }
48
- return hex.join('')
49
- }
50
- ```
51
-
52
- ## Basic AES Encryption
53
-
54
- ### Generating Encryption Keys
55
-
56
- The `SymmetricKey` class provides methods to create secure encryption keys:
57
-
58
- ```typescript
59
- // Generate a random 256-bit AES key
60
- const symmetricKey = SymmetricKey.fromRandom()
61
- console.log('Generated key:', symmetricKey.toHex())
62
-
63
- // Create a key from existing data (32 bytes)
64
- const keyData = Random(32)
65
- const customKey = new SymmetricKey(keyData)
66
- console.log('Custom key:', customKey.toHex())
67
-
68
- // Create a key from hex string
69
- const hexKey = 'a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456'
70
- const keyFromHex = new SymmetricKey(hexToBytes(hexKey))
71
- ```
72
-
73
- ### Encrypting Data
74
-
75
- The `encrypt` method supports both string and binary data:
76
-
77
- ```typescript
78
- const symmetricKey = SymmetricKey.fromRandom()
79
- const message = 'Hello, this is a secret message!'
80
-
81
- // Encrypt a string (UTF-8 encoding)
82
- const encryptedMessage = symmetricKey.encrypt(message) as number[]
83
- console.log('Encrypted message length:', encryptedMessage.length)
84
-
85
- // Encrypt binary data
86
- const binaryData = Utils.toArray('Binary data example', 'utf8')
87
- const encryptedBinary = symmetricKey.encrypt(binaryData) as number[]
88
-
89
- // Encrypt with hex output (using manual conversion for clarity)
90
- const encryptedBytes = symmetricKey.encrypt(message) as number[]
91
- const hexEncrypted = bytesToHex(encryptedBytes)
92
- console.log('Hex encrypted:', hexEncrypted)
93
-
94
- // Alternative: SDK's built-in hex handling (for hex input data)
95
- // Note: The 'hex' parameter treats the input as hex, not the output format
96
- const messageAsHex = Buffer.from(message).toString('hex') // Convert message to hex first
97
- const sdkHexEncrypted = symmetricKey.encrypt(messageAsHex, 'hex') as string
98
- console.log('SDK hex encrypted:', sdkHexEncrypted)
99
- ```
100
-
101
- ### Decrypting Data
102
-
103
- The `decrypt` method reverses the encryption process:
104
-
105
- ```typescript
106
- // Decrypt to string (UTF-8)
107
- const decryptedMessage = symmetricKey.decrypt(encryptedMessage, 'utf8') as string
108
- console.log('Decrypted message:', decryptedMessage)
109
-
110
- // Decrypt to binary array
111
- const decryptedBinary = symmetricKey.decrypt(encryptedBinary) as number[]
112
- console.log('Decrypted binary:', Utils.toUTF8(decryptedBinary))
113
-
114
- // Decrypt hex-encoded data (manual conversion method)
115
- const hexBytes = hexToBytes(hexEncrypted)
116
- const decryptedFromHex = symmetricKey.decrypt(hexBytes, 'utf8') as string
117
- console.log('Decrypted from hex:', decryptedFromHex)
118
-
119
- // Alternative: SDK hex handling method
120
- const sdkHexBytes = hexToBytes(sdkHexEncrypted)
121
- const sdkDecryptedHex = symmetricKey.decrypt(sdkHexBytes, 'hex') as string
122
- const sdkDecryptedMessage = Buffer.from(sdkDecryptedHex, 'hex').toString('utf8')
123
- console.log('SDK decrypted from hex:', sdkDecryptedMessage)
124
- ```
125
-
126
- ### Understanding the Hex Parameter
127
-
128
- The `enc` parameter in the SDK's `encrypt()` and `decrypt()` methods can be confusing. Here's how it actually works:
129
-
130
- **In `encrypt(data, enc)`:**
131
-
132
- - `enc` specifies how to **interpret the input data**
133
- - `enc: 'hex'` means the input data is a hex string that should be converted to bytes
134
- - The output format is determined by the `enc` parameter (hex string if `enc: 'hex'`, byte array otherwise)
135
-
136
- **In `decrypt(data, enc)`:**
137
-
138
- - `enc` specifies the **output format**
139
- - `enc: 'hex'` returns the decrypted data as a hex string
140
- - `enc: 'utf8'` returns the decrypted data as a UTF-8 string
141
-
142
- ```typescript
143
- // Example: Encrypting hex data with SDK's built-in hex handling
144
- const message = 'Hello, World!'
145
- const messageAsHex = Buffer.from(message).toString('hex') // '48656c6c6f2c20576f726c6421'
146
-
147
- // Encrypt hex input data
148
- const encrypted = key.encrypt(messageAsHex, 'hex') as string
149
-
150
- // Decrypt and get result as hex
151
- const decryptedHex = key.decrypt(hexToBytes(encrypted), 'hex') as string
152
- console.log('Decrypted as hex:', decryptedHex) // '48656c6c6f2c20576f726c6421'
153
-
154
- // Convert hex result back to UTF-8
155
- const finalMessage = Buffer.from(decryptedHex, 'hex').toString('utf8')
156
- console.log('Final message:', finalMessage) // 'Hello, World!'
157
- ```
158
-
159
- ## Complete Encryption Example
160
-
161
- Here's a comprehensive example demonstrating the full encryption workflow:
162
-
163
- ```typescript
164
- import { SymmetricKey, Utils } from '@bsv/sdk'
165
-
166
- function demonstrateAESEncryption() {
167
- console.log('=== AES Encryption Demonstration ===')
168
-
169
- // 1. Generate a random encryption key
170
- const symmetricKey = SymmetricKey.fromRandom()
171
- console.log('Generated key:', symmetricKey.toHex().substring(0, 16) + '...')
172
-
173
- // 2. Prepare the message to encrypt
174
- const originalMessage = 'This is a confidential message that needs protection!'
175
- console.log('Original message:', originalMessage)
176
-
177
- // 3. Encrypt the message
178
- const encryptedData = symmetricKey.encrypt(originalMessage) as number[]
179
- console.log('Encrypted data length:', encryptedData.length, 'bytes')
180
- console.log('Encrypted (first 32 bytes):', encryptedData.slice(0, 32))
181
-
182
- // 4. Decrypt the message
183
- const decryptedMessage = symmetricKey.decrypt(encryptedData, 'utf8') as string
184
-
185
- // 5. Verify integrity
186
- const isValid = originalMessage === decryptedMessage
187
- console.log('Decryption successful:', isValid)
188
-
189
- return {
190
- key: symmetricKey,
191
- original: originalMessage,
192
- encrypted: encryptedData,
193
- decrypted: decryptedMessage,
194
- valid: isValid
195
- }
196
- }
197
-
198
- // Run the demonstration
199
- const result = demonstrateAESEncryption()
200
- ```
201
-
202
- ## Working with Different Data Types
203
-
204
- ### Encrypting JSON Data
205
-
206
- ```typescript
207
- function encryptJSON(data: any, key: SymmetricKey): number[] {
208
- const jsonString = JSON.stringify(data)
209
- return key.encrypt(jsonString) as number[]
210
- }
211
-
212
- function decryptJSON(encryptedData: number[], key: SymmetricKey): any {
213
- const jsonString = key.decrypt(encryptedData, 'utf8') as string
214
- return JSON.parse(jsonString)
215
- }
216
-
217
- // Example usage
218
- const symmetricKey = SymmetricKey.fromRandom()
219
- const userData = {
220
- name: 'Alice',
221
- email: 'alice@example.com',
222
- balance: 1000,
223
- transactions: ['tx1', 'tx2', 'tx3']
224
- }
225
-
226
- const encryptedJSON = encryptJSON(userData, symmetricKey)
227
- const decryptedData = decryptJSON(encryptedJSON, symmetricKey)
228
- console.log('Original data:', userData)
229
- console.log('Decrypted data:', decryptedData)
230
- ```
231
-
232
- ### Encrypting Files and Large Data
233
-
234
- ```typescript
235
- function encryptLargeData(data: string, key: SymmetricKey): {
236
- encrypted: number[],
237
- size: number,
238
- checksum: string
239
- } {
240
- // Convert to binary
241
- const binaryData = Utils.toArray(data, 'utf8')
242
-
243
- // Encrypt the data
244
- const encrypted = key.encrypt(binaryData) as number[]
245
-
246
- // Calculate checksum of original data for verification
247
- const checksum = Utils.toBase64(binaryData.slice(0, 32))
248
-
249
- return {
250
- encrypted,
251
- size: binaryData.length,
252
- checksum
253
- }
254
- }
255
-
256
- function decryptLargeData(encryptedData: {
257
- encrypted: number[],
258
- size: number,
259
- checksum: string
260
- }, key: SymmetricKey): string {
261
- // Decrypt the data
262
- const decrypted = key.decrypt(encryptedData.encrypted) as number[]
263
-
264
- // Verify size
265
- if (decrypted.length !== encryptedData.size) {
266
- throw new Error('Decrypted data size mismatch')
267
- }
268
-
269
- // Verify checksum
270
- const checksum = Utils.toBase64(decrypted.slice(0, 32))
271
- if (checksum !== encryptedData.checksum) {
272
- console.warn('Checksum mismatch - data may be corrupted')
273
- }
274
-
275
- return Utils.toUTF8(decrypted)
276
- }
277
- ```
278
-
279
- ## Key Derivation and Management
280
-
281
- ### Deriving Keys from Passwords
282
-
283
- ```typescript
284
- import { Hash } from '@bsv/sdk'
285
-
286
- function deriveKeyFromPassword(password: string, salt?: number[]): SymmetricKey {
287
- // Use provided salt or generate random one
288
- const keySalt = salt || Random(32)
289
-
290
- // Create key material by hashing password + salt
291
- const passwordBytes = Utils.toArray(password, 'utf8')
292
- const keyMaterial = [...passwordBytes, ...keySalt]
293
-
294
- // Hash to create 256-bit key
295
- const keyHash = Hash.sha256(keyMaterial)
296
-
297
- return new SymmetricKey(keyHash)
298
- }
299
-
300
- // Example usage
301
- const password = 'MySecurePassword123!'
302
- const salt = Random(32)
303
- const derivedKey = deriveKeyFromPassword(password, salt)
304
-
305
- // Store salt separately - needed for key recreation
306
- console.log('Derived key:', derivedKey.toHex())
307
- console.log('Salt (store this):', Utils.toBase64(salt))
308
-
309
- // Recreate the same key later
310
- const recreatedKey = deriveKeyFromPassword(password, salt)
311
- console.log('Keys match:', derivedKey.toHex() === recreatedKey.toHex())
312
- ```
313
-
314
- ### Key Rotation and Versioning
315
-
316
- ```typescript
317
- class KeyManager {
318
- private keys: Map<number, SymmetricKey> = new Map()
319
- private currentVersion: number = 1
320
-
321
- generateNewKey(): number {
322
- const newKey = SymmetricKey.fromRandom()
323
- this.keys.set(this.currentVersion, newKey)
324
- return this.currentVersion++
325
- }
326
-
327
- encrypt(data: string, version?: number): {
328
- encrypted: number[],
329
- version: number
330
- } {
331
- const keyVersion = version || this.currentVersion - 1
332
- const key = this.keys.get(keyVersion)
333
-
334
- if (!key) {
335
- throw new Error(`Key version ${keyVersion} not found`)
336
- }
337
-
338
- return {
339
- encrypted: key.encrypt(data) as number[],
340
- version: keyVersion
341
- }
342
- }
343
-
344
- decrypt(encryptedData: {
345
- encrypted: number[],
346
- version: number
347
- }): string {
348
- const key = this.keys.get(encryptedData.version)
349
-
350
- if (!key) {
351
- throw new Error(`Key version ${encryptedData.version} not found`)
352
- }
353
-
354
- return key.decrypt(encryptedData.encrypted, 'utf8') as string
355
- }
356
- }
357
-
358
- // Example usage
359
- const keyManager = new KeyManager()
360
- const v1 = keyManager.generateNewKey()
361
- const v2 = keyManager.generateNewKey()
362
-
363
- const message = 'Data encrypted with version 1'
364
- const encrypted = keyManager.encrypt(message, v1)
365
- const decrypted = keyManager.decrypt(encrypted)
366
- console.log('Decrypted:', decrypted)
367
- ```
368
-
369
- ## Combining AES with ECDH
370
-
371
- For secure communication between parties, combine AES encryption with ECDH key exchange:
372
-
373
- ```typescript
374
- import { PrivateKey, PublicKey } from '@bsv/sdk'
375
-
376
- function createSecureChannel(
377
- senderPrivateKey: PrivateKey,
378
- recipientPublicKey: PublicKey
379
- ): SymmetricKey {
380
- // Derive shared secret using ECDH
381
- const sharedSecret = senderPrivateKey.deriveSharedSecret(recipientPublicKey)
382
-
383
- // Use shared secret as AES key material
384
- const keyMaterial = sharedSecret.encode(true).slice(1) // Remove prefix byte
385
-
386
- return new SymmetricKey(keyMaterial)
387
- }
388
-
389
- function secureMessageExchange() {
390
- // Generate key pairs for Alice and Bob
391
- const alicePrivate = PrivateKey.fromRandom()
392
- const alicePublic = alicePrivate.toPublicKey()
393
-
394
- const bobPrivate = PrivateKey.fromRandom()
395
- const bobPublic = bobPrivate.toPublicKey()
396
-
397
- // Alice creates encryption key using Bob's public key
398
- const aliceEncryptionKey = createSecureChannel(alicePrivate, bobPublic)
399
-
400
- // Bob creates the same encryption key using Alice's public key
401
- const bobDecryptionKey = createSecureChannel(bobPrivate, alicePublic)
402
-
403
- // Verify both parties have the same key
404
- console.log('Keys match:',
405
- aliceEncryptionKey.toHex() === bobDecryptionKey.toHex())
406
-
407
- // Alice encrypts a message
408
- const message = 'Hello Bob, this is a secure message from Alice!'
409
- const encrypted = aliceEncryptionKey.encrypt(message) as number[]
410
-
411
- // Bob decrypts the message
412
- const decrypted = bobDecryptionKey.decrypt(encrypted, 'utf8') as string
413
-
414
- console.log('Original message:', message)
415
- console.log('Decrypted message:', decrypted)
416
- console.log('Secure communication successful:', message === decrypted)
417
-
418
- return { aliceEncryptionKey, bobDecryptionKey, message, decrypted }
419
- }
420
-
421
- // Run secure message exchange
422
- secureMessageExchange()
423
- ```
424
-
425
- ## Error Handling and Security
426
-
427
- ### Robust Encryption with Error Handling
428
-
429
- ```typescript
430
- class SecureAESManager {
431
- private key: SymmetricKey
432
-
433
- constructor(key?: SymmetricKey) {
434
- this.key = key || SymmetricKey.fromRandom()
435
- }
436
-
437
- safeEncrypt(data: string): {
438
- success: boolean,
439
- encrypted?: number[],
440
- error?: string
441
- } {
442
- try {
443
- if (!data || data.length === 0) {
444
- return { success: false, error: 'Data cannot be empty' }
445
- }
446
-
447
- const encrypted = this.key.encrypt(data) as number[]
448
-
449
- if (!encrypted || encrypted.length === 0) {
450
- return { success: false, error: 'Encryption failed' }
451
- }
452
-
453
- return { success: true, encrypted }
454
- } catch (error) {
455
- return {
456
- success: false,
457
- error: `Encryption error: ${error.message}`
458
- }
459
- }
460
- }
461
-
462
- safeDecrypt(encryptedData: number[]): {
463
- success: boolean,
464
- decrypted?: string,
465
- error?: string
466
- } {
467
- try {
468
- if (!encryptedData || encryptedData.length === 0) {
469
- return { success: false, error: 'Encrypted data cannot be empty' }
470
- }
471
-
472
- const decrypted = this.key.decrypt(encryptedData, 'utf8') as string
473
-
474
- return { success: true, decrypted }
475
- } catch (error) {
476
- return {
477
- success: false,
478
- error: `Decryption failed: ${error.message}`
479
- }
480
- }
481
- }
482
-
483
- rotateKey(): void {
484
- this.key = SymmetricKey.fromRandom()
485
- }
486
-
487
- getKeyFingerprint(): string {
488
- // Create a fingerprint of the key for identification
489
- const keyBytes = this.key.toArray()
490
- const hash = Hash.sha256(keyBytes)
491
- return Utils.toBase64(hash).substring(0, 16)
492
- }
493
- }
494
-
495
- // Example usage with error handling
496
- const aesManager = new SecureAESManager()
497
- console.log('Key fingerprint:', aesManager.getKeyFingerprint())
498
-
499
- const testData = 'Sensitive information that needs protection'
500
- const encryptResult = aesManager.safeEncrypt(testData)
501
-
502
- if (encryptResult.success) {
503
- console.log('Encryption successful')
504
-
505
- const decryptResult = aesManager.safeDecrypt(encryptResult.encrypted!)
506
-
507
- if (decryptResult.success) {
508
- console.log('Decryption successful:', decryptResult.decrypted)
509
- } else {
510
- console.error('Decryption failed:', decryptResult.error)
511
- }
512
- } else {
513
- console.error('Encryption failed:', encryptResult.error)
514
- }
515
- ```
516
-
517
- ## Practical Applications
518
-
519
- ### Encrypting Transaction Metadata
520
-
521
- ```typescript
522
- import { Transaction, PrivateKey } from '@bsv/sdk'
523
-
524
- function encryptTransactionMetadata(
525
- transaction: Transaction,
526
- metadata: any,
527
- encryptionKey: SymmetricKey
528
- ): number[] {
529
- const metadataWithTx = {
530
- txid: Buffer.from(transaction.id()).toString('hex'),
531
- timestamp: Date.now(),
532
- metadata: metadata
533
- }
534
-
535
- const jsonString = JSON.stringify(metadataWithTx)
536
- return encryptionKey.encrypt(jsonString) as number[]
537
- }
538
-
539
- function decryptTransactionMetadata(
540
- encryptedMetadata: number[],
541
- encryptionKey: SymmetricKey
542
- ): any {
543
- const jsonString = encryptionKey.decrypt(encryptedMetadata, 'utf8') as string
544
- return JSON.parse(jsonString)
545
- }
546
-
547
- // Example: Encrypt private notes about a transaction
548
- const privateKey = PrivateKey.fromRandom()
549
- const encryptionKey = SymmetricKey.fromRandom()
550
-
551
- // Create a simple transaction (placeholder)
552
- const transaction = new Transaction()
553
- // ... transaction setup ...
554
-
555
- const privateMetadata = {
556
- purpose: 'Payment to supplier',
557
- invoiceNumber: 'INV-2024-001',
558
- notes: 'Quarterly payment for services',
559
- category: 'business-expense'
560
- }
561
-
562
- const encrypted = encryptTransactionMetadata(transaction, privateMetadata, encryptionKey)
563
- const decrypted = decryptTransactionMetadata(encrypted, encryptionKey)
564
-
565
- console.log('Original metadata:', privateMetadata)
566
- console.log('Decrypted metadata:', decrypted)
567
- ```
568
-
569
- ### Secure Configuration Storage
570
-
571
- ```typescript
572
- class SecureConfig {
573
- private encryptionKey: SymmetricKey
574
- private config: Map<string, number[]> = new Map()
575
-
576
- constructor(password: string) {
577
- // Derive encryption key from password
578
- const salt = Random(32)
579
- const passwordHash = Hash.sha256([
580
- ...Utils.toArray(password, 'utf8'),
581
- ...salt
582
- ])
583
- this.encryptionKey = new SymmetricKey(passwordHash)
584
- }
585
-
586
- set(key: string, value: any): void {
587
- const jsonValue = JSON.stringify(value)
588
- const encrypted = this.encryptionKey.encrypt(jsonValue) as number[]
589
- this.config.set(key, encrypted)
590
- }
591
-
592
- get(key: string): any {
593
- const encrypted = this.config.get(key)
594
- if (!encrypted) {
595
- return undefined
596
- }
597
-
598
- try {
599
- const decrypted = this.encryptionKey.decrypt(encrypted, 'utf8') as string
600
- return JSON.parse(decrypted)
601
- } catch (error) {
602
- console.error('Failed to decrypt config value:', error)
603
- return undefined
604
- }
605
- }
606
-
607
- has(key: string): boolean {
608
- return this.config.has(key)
609
- }
610
-
611
- delete(key: string): boolean {
612
- return this.config.delete(key)
613
- }
614
-
615
- export(): string {
616
- const exportData = {}
617
- for (const [key, encrypted] of this.config.entries()) {
618
- exportData[key] = Utils.toBase64(encrypted)
619
- }
620
- return JSON.stringify(exportData)
621
- }
622
-
623
- import(data: string): void {
624
- const importData = JSON.parse(data)
625
- for (const [key, base64Value] of Object.entries(importData)) {
626
- const encrypted = Utils.fromBase64(base64Value as string)
627
- this.config.set(key, encrypted)
628
- }
629
- }
630
- }
631
-
632
- // Example usage
633
- const secureConfig = new SecureConfig('MySecurePassword123!')
634
-
635
- // Store sensitive configuration
636
- secureConfig.set('apiKey', 'sk-1234567890abcdef')
637
- secureConfig.set('databaseUrl', 'postgresql://user:pass@host:5432/db')
638
- secureConfig.set('walletSeed', 'abandon abandon abandon...')
639
-
640
- // Retrieve configuration
641
- console.log('API Key:', secureConfig.get('apiKey'))
642
- console.log('Has database URL:', secureConfig.has('databaseUrl'))
643
-
644
- // Export encrypted configuration
645
- const exportedConfig = secureConfig.export()
646
- console.log('Exported config length:', exportedConfig.length)
647
- ```
648
-
649
- ## Performance Considerations
650
-
651
- ### Benchmarking AES Operations
652
-
653
- ```typescript
654
- function benchmarkAESPerformance() {
655
- const key = SymmetricKey.fromRandom()
656
- const testSizes = [100, 1000, 10000, 100000] // bytes
657
-
658
- console.log('=== AES Performance Benchmark ===')
659
-
660
- for (const size of testSizes) {
661
- const testData = 'x'.repeat(size)
662
-
663
- // Benchmark encryption
664
- const encryptStart = performance.now()
665
- const encrypted = key.encrypt(testData) as number[]
666
- const encryptTime = performance.now() - encryptStart
667
-
668
- // Benchmark decryption
669
- const decryptStart = performance.now()
670
- const decrypted = key.decrypt(encrypted, 'utf8') as string
671
- const decryptTime = performance.now() - decryptStart
672
-
673
- console.log(`Size: ${size} bytes`)
674
- console.log(` Encrypt: ${encryptTime.toFixed(2)}ms`)
675
- console.log(` Decrypt: ${decryptTime.toFixed(2)}ms`)
676
- console.log(` Total: ${(encryptTime + decryptTime).toFixed(2)}ms`)
677
- console.log(` Throughput: ${(size / (encryptTime + decryptTime) * 1000).toFixed(0)} bytes/sec`)
678
- console.log()
679
- }
680
- }
681
-
682
- // Run benchmark
683
- benchmarkAESPerformance()
684
- ```
685
-
686
- ## Security Best Practices
687
-
688
- ### Key Security Guidelines
689
-
690
- ```typescript
691
- class SecureKeyPractices {
692
- // Good: Generate random keys
693
- static generateSecureKey(): SymmetricKey {
694
- return SymmetricKey.fromRandom()
695
- }
696
-
697
- // Good: Derive keys from strong passwords
698
- static deriveFromPassword(password: string, salt: number[]): SymmetricKey {
699
- if (password.length < 12) {
700
- throw new Error('Password must be at least 12 characters')
701
- }
702
-
703
- const keyMaterial = Hash.sha256([
704
- ...Utils.toArray(password, 'utf8'),
705
- ...salt
706
- ])
707
- return new SymmetricKey(keyMaterial)
708
- }
709
-
710
- // Good: Secure key comparison
711
- static keysEqual(key1: SymmetricKey, key2: SymmetricKey): boolean {
712
- const bytes1 = key1.toArray()
713
- const bytes2 = key2.toArray()
714
-
715
- if (bytes1.length !== bytes2.length) {
716
- return false
717
- }
718
-
719
- // Constant-time comparison to prevent timing attacks
720
- let result = 0
721
- for (let i = 0; i < bytes1.length; i++) {
722
- result |= bytes1[i] ^ bytes2[i]
723
- }
724
- return result === 0
725
- }
726
-
727
- // Good: Secure key destruction
728
- static destroyKey(key: SymmetricKey): void {
729
- // Overwrite key material (note: this is conceptual in JavaScript)
730
- const keyArray = key.toArray()
731
- for (let i = 0; i < keyArray.length; i++) {
732
- keyArray[i] = 0
733
- }
734
- }
735
- }
736
-
737
- // Security validation example
738
- function validateSecurityPractices() {
739
- console.log('=== Security Practices Validation ===')
740
-
741
- // Generate secure keys
742
- const key1 = SecureKeyPractices.generateSecureKey()
743
- const key2 = SecureKeyPractices.generateSecureKey()
744
-
745
- console.log('Keys are different:', !SecureKeyPractices.keysEqual(key1, key2))
746
-
747
- // Test password-based key derivation
748
- const password = 'MyVerySecurePassword123!'
749
- const salt = Random(32)
750
- const derivedKey1 = SecureKeyPractices.deriveFromPassword(password, salt)
751
- const derivedKey2 = SecureKeyPractices.deriveFromPassword(password, salt)
752
-
753
- console.log('Derived keys are identical:',
754
- SecureKeyPractices.keysEqual(derivedKey1, derivedKey2))
755
-
756
- // Clean up
757
- SecureKeyPractices.destroyKey(key1)
758
- SecureKeyPractices.destroyKey(key2)
759
-
760
- console.log('Security validation complete')
761
- }
762
-
763
- validateSecurityPractices()
764
- ```
765
-
766
- ## Troubleshooting Common Issues
767
-
768
- ### Common Problems and Solutions
769
-
770
- ```typescript
771
- function troubleshootAESIssues() {
772
- console.log('=== AES Troubleshooting Guide ===')
773
-
774
- // Issue 1: Decryption fails with "Decryption failed!" error
775
- try {
776
- const key = SymmetricKey.fromRandom()
777
- const message = 'Test message'
778
- const encrypted = key.encrypt(message) as number[]
779
-
780
- // Corrupt the encrypted data to simulate tampering
781
- encrypted[10] = encrypted[10] ^ 1
782
-
783
- const decrypted = key.decrypt(encrypted, 'utf8')
784
- } catch (error) {
785
- console.log('Detected tampered data:', error.message)
786
- console.log(' Solution: Verify data integrity, check for transmission errors')
787
- }
788
-
789
- // Issue 2: Wrong key used for decryption
790
- try {
791
- const key1 = SymmetricKey.fromRandom()
792
- const key2 = SymmetricKey.fromRandom()
793
- const message = 'Test message'
794
-
795
- const encrypted = key1.encrypt(message) as number[]
796
- const decrypted = key2.decrypt(encrypted, 'utf8') // Wrong key!
797
- } catch (error) {
798
- console.log('Detected wrong key usage:', error.message)
799
- console.log(' Solution: Ensure same key is used for encryption and decryption')
800
- }
801
-
802
- // Issue 3: Empty or invalid data
803
- try {
804
- const key = SymmetricKey.fromRandom()
805
- // Empty strings are actually supported and work fine
806
- const encrypted = key.encrypt('') as number[]
807
- const decrypted = key.decrypt(encrypted, 'utf8') as string
808
- console.log('Empty string encryption works:', decrypted === '')
809
- console.log(' Note: Empty strings are supported by the SDK')
810
- } catch (error) {
811
- console.log('Unexpected error with empty data:', error.message)
812
- }
813
-
814
- // Issue 4: Hex string handling
815
- try {
816
- const hexKey = 'a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456'
817
- // This will fail - constructor expects byte array, not hex string
818
- // const wrongKey = new SymmetricKey(hexKey)
819
-
820
- // Correct approach - convert hex to bytes first
821
- const correctKey = new SymmetricKey(hexToBytes(hexKey))
822
- console.log('Hex key creation works with helper function')
823
- console.log(' Solution: Use hexToBytes() helper function for hex strings')
824
- console.log(' Note: SDK does not provide Utils.fromHex() method')
825
- } catch (error) {
826
- console.log('Hex key creation issue:', error.message)
827
- }
828
-
829
- console.log('\nTroubleshooting complete')
830
- }
831
-
832
- troubleshootAESIssues()
833
- ```
834
-
835
- ## Testing Your Implementation
836
-
837
- ### Comprehensive Test Suite
838
-
839
- ```typescript
840
- function runAESTests() {
841
- console.log('=== AES Implementation Tests ===')
842
- let passed = 0
843
- let total = 0
844
-
845
- function test(name: string, testFn: () => boolean) {
846
- total++
847
- try {
848
- const result = testFn()
849
- if (result) {
850
- console.log(` ${name}`)
851
- passed++
852
- } else {
853
- console.log(` ${name}`)
854
- }
855
- } catch (error) {
856
- console.log(` ${name} - Error: ${error.message}`)
857
- }
858
- }
859
-
860
- // Test 1: Basic encryption/decryption
861
- test('Basic encryption/decryption', () => {
862
- const key = SymmetricKey.fromRandom()
863
- const message = 'Hello, World!'
864
- const encrypted = key.encrypt(message) as number[]
865
- const decrypted = key.decrypt(encrypted, 'utf8') as string
866
- return message === decrypted
867
- })
868
-
869
- // Test 2: Binary data handling
870
- test('Binary data encryption', () => {
871
- const key = SymmetricKey.fromRandom()
872
- const binaryData = Random(100)
873
- const encrypted = key.encrypt(binaryData) as number[]
874
- const decrypted = key.decrypt(encrypted) as number[]
875
- return JSON.stringify(binaryData) === JSON.stringify(decrypted)
876
- })
877
-
878
- // Test 3: Large data encryption
879
- test('Large data encryption', () => {
880
- const key = SymmetricKey.fromRandom()
881
- const largeMessage = 'x'.repeat(10000)
882
- const encrypted = key.encrypt(largeMessage) as number[]
883
- const decrypted = key.decrypt(encrypted, 'utf8') as string
884
- return largeMessage === decrypted
885
- })
886
-
887
- // Test 4: Key derivation consistency
888
- test('Key derivation consistency', () => {
889
- const password = 'TestPassword123'
890
- const salt = Random(32)
891
- const key1 = new SymmetricKey(Hash.sha256([
892
- ...Utils.toArray(password, 'utf8'),
893
- ...salt
894
- ]))
895
- const key2 = new SymmetricKey(Hash.sha256([
896
- ...Utils.toArray(password, 'utf8'),
897
- ...salt
898
- ]))
899
- return key1.toHex() === key2.toHex()
900
- })
901
-
902
- // Test 5: Hex encoding/decoding
903
- test('Hex encoding support', () => {
904
- const key = SymmetricKey.fromRandom()
905
- const message = 'Test message'
906
- const encryptedBytes = key.encrypt(message) as number[]
907
- const encrypted = bytesToHex(encryptedBytes)
908
- const hexBytes = hexToBytes(encrypted)
909
- const decrypted = key.decrypt(hexBytes, 'utf8') as string
910
- return message === decrypted
911
- })
912
-
913
- console.log(`\nTests completed: ${passed}/${total} passed`)
914
- return passed === total
915
- }
916
-
917
- // Run the test suite
918
- const allTestsPassed = runAESTests()
919
- console.log('\nAll tests passed:', allTestsPassed)
920
- ```
921
-
922
- ## Summary
923
-
924
- In this tutorial, you've learned how to:
925
-
926
- 1. **Generate secure AES encryption keys** using `SymmetricKey.fromRandom()`
927
- 2. **Encrypt and decrypt data** with the `encrypt()` and `decrypt()` methods
928
- 3. **Handle different data formats** including strings, binary data, and JSON
929
- 4. **Derive keys from passwords** using secure hashing techniques
930
- 5. **Implement key management** with versioning and rotation
931
- 6. **Combine AES with ECDH** for secure communication channels
932
- 7. **Apply security best practices** including error handling and validation
933
- 8. **Build practical applications** like secure configuration storage
934
- 9. **Optimize performance** and troubleshoot common issues
935
-
936
- The BSV TypeScript SDK's `SymmetricKey` class provides a robust, secure implementation of AES-GCM encryption that's suitable for production applications. The built-in authentication prevents tampering, while the straightforward API makes it easy to integrate encryption into your Bitcoin applications.
937
-
938
- ## Next Steps
939
-
940
- - Explore the [ECDH Key Exchange](./ecdh-key-exchange.md) tutorial for asymmetric encryption
941
- - Learn about [Messages Reference](../reference/messages.md) for authentication
942
- - Study [Advanced Transaction Construction](./advanced-transaction.md) for complex applications
943
- - Review the [Security Best Practices](../guides/security-best-practices.md) guide
944
-
945
- ## Additional Resources
946
-
947
- - [AES Wikipedia](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)
948
- - [GCM Mode Wikipedia](https://en.wikipedia.org/wiki/Galois/Counter_Mode)
949
- - [BSV SDK Primitives Reference](../reference/primitives.md)