@bsv/sdk 1.9.3 → 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.
- package/dist/cjs/package.json +1 -1
- package/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- package/docs/MARKDOWN_VALIDATION_GUIDE.md +0 -175
- package/docs/concepts/beef.md +0 -92
- package/docs/concepts/chain-tracking.md +0 -134
- package/docs/concepts/decentralized-identity.md +0 -221
- package/docs/concepts/fees.md +0 -249
- package/docs/concepts/identity-certificates.md +0 -307
- package/docs/concepts/index.md +0 -77
- package/docs/concepts/key-management.md +0 -185
- package/docs/concepts/script-templates.md +0 -176
- package/docs/concepts/sdk-philosophy.md +0 -80
- package/docs/concepts/signatures.md +0 -194
- package/docs/concepts/spv-verification.md +0 -118
- package/docs/concepts/transaction-encoding.md +0 -167
- package/docs/concepts/transaction-structure.md +0 -67
- package/docs/concepts/trust-model.md +0 -139
- package/docs/concepts/verification.md +0 -250
- package/docs/concepts/wallet-integration.md +0 -101
- package/docs/guides/development-wallet-setup.md +0 -374
- package/docs/guides/direct-transaction-creation.md +0 -147
- package/docs/guides/http-client-configuration.md +0 -488
- package/docs/guides/index.md +0 -138
- package/docs/guides/large-transactions.md +0 -448
- package/docs/guides/multisig-transactions.md +0 -792
- package/docs/guides/security-best-practices.md +0 -494
- package/docs/guides/transaction-batching.md +0 -132
- package/docs/guides/transaction-signing-methods.md +0 -419
- package/docs/reference/arc-config.md +0 -698
- package/docs/reference/brc-100.md +0 -33
- package/docs/reference/configuration.md +0 -835
- package/docs/reference/debugging.md +0 -705
- package/docs/reference/errors.md +0 -597
- package/docs/reference/index.md +0 -111
- package/docs/reference/network-config.md +0 -914
- package/docs/reference/op-codes.md +0 -325
- package/docs/reference/transaction-signatures.md +0 -95
- package/docs/tutorials/advanced-transaction.md +0 -572
- package/docs/tutorials/aes-encryption.md +0 -949
- package/docs/tutorials/authfetch-tutorial.md +0 -986
- package/docs/tutorials/ecdh-key-exchange.md +0 -549
- package/docs/tutorials/elliptic-curve-fundamentals.md +0 -606
- package/docs/tutorials/error-handling.md +0 -1216
- package/docs/tutorials/first-transaction-low-level.md +0 -205
- package/docs/tutorials/first-transaction.md +0 -275
- package/docs/tutorials/hashes-and-hmacs.md +0 -788
- package/docs/tutorials/identity-management.md +0 -729
- package/docs/tutorials/index.md +0 -219
- package/docs/tutorials/key-management.md +0 -538
- package/docs/tutorials/protowallet-development.md +0 -743
- package/docs/tutorials/script-construction.md +0 -690
- package/docs/tutorials/spv-merkle-proofs.md +0 -685
- package/docs/tutorials/testnet-transactions-low-level.md +0 -359
- package/docs/tutorials/transaction-broadcasting.md +0 -538
- package/docs/tutorials/transaction-types.md +0 -420
- package/docs/tutorials/type-42.md +0 -568
- 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)
|