@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,788 +0,0 @@
|
|
|
1
|
-
# Cryptographic Hashing and HMACs
|
|
2
|
-
|
|
3
|
-
**Duration**: 75 minutes
|
|
4
|
-
**Prerequisites**: Basic TypeScript knowledge, understanding of cryptographic concepts
|
|
5
|
-
|
|
6
|
-
## Learning Goals
|
|
7
|
-
|
|
8
|
-
By completing this tutorial, you will:
|
|
9
|
-
|
|
10
|
-
- Understand cryptographic hash functions and their properties
|
|
11
|
-
- Master the Hash module classes and helper functions in the BSV TypeScript SDK
|
|
12
|
-
- Implement various hash algorithms (SHA-256, SHA-512, SHA-1, RIPEMD-160)
|
|
13
|
-
- Create and verify HMACs for message authentication
|
|
14
|
-
- Apply Bitcoin-specific hashing patterns (hash256, hash160)
|
|
15
|
-
- Build practical applications using hashing for data integrity and authentication
|
|
16
|
-
- Understand performance considerations and security best practices
|
|
17
|
-
|
|
18
|
-
## Introduction to Cryptographic Hashing
|
|
19
|
-
|
|
20
|
-
Cryptographic hash functions are mathematical algorithms that transform input data of any size into fixed-size output values. They are fundamental to Bitcoin's security architecture, providing:
|
|
21
|
-
|
|
22
|
-
- **Data Integrity**: Detect any changes to data
|
|
23
|
-
- **Digital Fingerprints**: Unique identifiers for data
|
|
24
|
-
- **Proof of Work**: Foundation for Bitcoin's consensus mechanism
|
|
25
|
-
- **Address Generation**: Converting public keys to Bitcoin addresses
|
|
26
|
-
|
|
27
|
-
The BSV TypeScript SDK provides comprehensive hashing capabilities through the `Hash` module, supporting both class-based and functional approaches.
|
|
28
|
-
|
|
29
|
-
## Setting Up Your Environment
|
|
30
|
-
|
|
31
|
-
First, import the necessary modules from the BSV SDK:
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { Hash, Utils } from '@bsv/sdk'
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The `Hash` module contains:
|
|
38
|
-
|
|
39
|
-
- Hash function classes (`SHA256`, `SHA512`, `SHA1`, `RIPEMD160`)
|
|
40
|
-
- HMAC classes (`SHA256HMAC`, `SHA512HMAC`, `SHA1HMAC`)
|
|
41
|
-
- Helper functions (`sha256`, `sha512`, `hash256`, `hash160`, `sha256hmac`)
|
|
42
|
-
- Utility functions for data conversion and encoding
|
|
43
|
-
|
|
44
|
-
## Basic Hash Function Usage
|
|
45
|
-
|
|
46
|
-
### SHA-256 Hashing
|
|
47
|
-
|
|
48
|
-
SHA-256 is Bitcoin's primary hash function. Here's how to use it:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
// Method 1: Using the SHA256 class
|
|
52
|
-
const sha256Hasher = new Hash.SHA256()
|
|
53
|
-
sha256Hasher.update('Message to hash')
|
|
54
|
-
const hashedMessage = sha256Hasher.digestHex()
|
|
55
|
-
console.log('SHA-256 hash:', hashedMessage)
|
|
56
|
-
// Output: f1aa45b0f5f6703468f9b9bc2b9874d4fa6b001a170d0f132aa5a26d00d0c7e5
|
|
57
|
-
|
|
58
|
-
// Method 2: Using the helper function
|
|
59
|
-
const message = 'Hello, Bitcoin!'
|
|
60
|
-
const hashResult = Hash.sha256(Utils.toArray(message, 'utf8'))
|
|
61
|
-
console.log('SHA-256 hash (binary):', hashResult)
|
|
62
|
-
|
|
63
|
-
// Convert to hex for display
|
|
64
|
-
const hashHex = Utils.toHex(hashResult)
|
|
65
|
-
console.log('SHA-256 hash (hex):', hashHex)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Working with Different Data Types
|
|
69
|
-
|
|
70
|
-
The Hash functions can process various data formats:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
// String input
|
|
74
|
-
const stringHash = Hash.sha256(Utils.toArray('Hello World', 'utf8'))
|
|
75
|
-
|
|
76
|
-
// Hex string input
|
|
77
|
-
const hexHash = Hash.sha256(Utils.toArray('deadbeef', 'hex'))
|
|
78
|
-
|
|
79
|
-
// Binary array input
|
|
80
|
-
const binaryData = [0x01, 0x02, 0x03, 0x04]
|
|
81
|
-
const binaryHash = Hash.sha256(binaryData)
|
|
82
|
-
|
|
83
|
-
// JSON data hashing
|
|
84
|
-
const jsonData = { name: 'Alice', amount: 100 }
|
|
85
|
-
const jsonString = JSON.stringify(jsonData)
|
|
86
|
-
const jsonHash = Hash.sha256(Utils.toArray(jsonString, 'utf8'))
|
|
87
|
-
|
|
88
|
-
console.log('String hash:', Utils.toHex(stringHash))
|
|
89
|
-
console.log('Hex hash:', Utils.toHex(hexHash))
|
|
90
|
-
console.log('Binary hash:', Utils.toHex(binaryHash))
|
|
91
|
-
console.log('JSON hash:', Utils.toHex(jsonHash))
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Other Hash Algorithms
|
|
95
|
-
|
|
96
|
-
The SDK supports multiple hash algorithms:
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// SHA-512
|
|
100
|
-
const sha512Hasher = new Hash.SHA512()
|
|
101
|
-
sha512Hasher.update('Message for SHA-512')
|
|
102
|
-
const sha512Result = sha512Hasher.digestHex()
|
|
103
|
-
console.log('SHA-512 hash:', sha512Result)
|
|
104
|
-
|
|
105
|
-
// SHA-1 (legacy, use with caution)
|
|
106
|
-
const sha1Hasher = new Hash.SHA1()
|
|
107
|
-
sha1Hasher.update('Message for SHA-1')
|
|
108
|
-
const sha1Result = sha1Hasher.digestHex()
|
|
109
|
-
console.log('SHA-1 hash:', sha1Result)
|
|
110
|
-
|
|
111
|
-
// RIPEMD-160 (used in Bitcoin address generation)
|
|
112
|
-
const ripemdHasher = new Hash.RIPEMD160()
|
|
113
|
-
ripemdHasher.update('Message for RIPEMD-160')
|
|
114
|
-
const ripemdResult = ripemdHasher.digestHex()
|
|
115
|
-
console.log('RIPEMD-160 hash:', ripemdResult)
|
|
116
|
-
|
|
117
|
-
// Using helper functions
|
|
118
|
-
const sha512Helper = Hash.sha512(Utils.toArray('Hello', 'utf8'))
|
|
119
|
-
console.log('SHA-512 helper result:', Utils.toHex(sha512Helper))
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Bitcoin-Specific Hash Functions
|
|
123
|
-
|
|
124
|
-
### Double SHA-256 (hash256)
|
|
125
|
-
|
|
126
|
-
Bitcoin uses double SHA-256 hashing for block headers and transaction IDs:
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
// Double SHA-256 using hash256 helper
|
|
130
|
-
const message = 'Bitcoin transaction data'
|
|
131
|
-
const doubleHash = Hash.hash256(Utils.toArray(message, 'utf8'))
|
|
132
|
-
console.log('Double SHA-256 hash:', Utils.toHex(doubleHash))
|
|
133
|
-
|
|
134
|
-
// Manual double hashing
|
|
135
|
-
const firstHash = Hash.sha256(Utils.toArray(message, 'utf8'))
|
|
136
|
-
const secondHash = Hash.sha256(firstHash)
|
|
137
|
-
console.log('Manual double hash:', Utils.toHex(secondHash))
|
|
138
|
-
|
|
139
|
-
// Both methods produce the same result
|
|
140
|
-
console.log('Results match:', Utils.toHex(doubleHash) === Utils.toHex(secondHash))
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Hash160 (SHA-256 + RIPEMD-160)
|
|
144
|
-
|
|
145
|
-
Used for Bitcoin address generation:
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
// Hash160: SHA-256 followed by RIPEMD-160
|
|
149
|
-
const publicKeyData = 'compressed_public_key_hex_data'
|
|
150
|
-
const hash160Result = Hash.hash160(Utils.toArray(publicKeyData, 'hex'))
|
|
151
|
-
console.log('Hash160 result:', Utils.toHex(hash160Result))
|
|
152
|
-
|
|
153
|
-
// Manual implementation
|
|
154
|
-
const sha256First = Hash.sha256(Utils.toArray(publicKeyData, 'hex'))
|
|
155
|
-
const ripemd160Second = new Hash.RIPEMD160().update(sha256First).digest()
|
|
156
|
-
console.log('Manual Hash160:', Utils.toHex(ripemd160Second))
|
|
157
|
-
|
|
158
|
-
// Results should match
|
|
159
|
-
console.log('Hash160 results match:',
|
|
160
|
-
Utils.toHex(hash160Result) === Utils.toHex(ripemd160Second))
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## HMAC Implementation
|
|
164
|
-
|
|
165
|
-
HMACs (Hash-based Message Authentication Codes) provide both data integrity and authentication by incorporating a secret key into the hashing process.
|
|
166
|
-
|
|
167
|
-
### Basic HMAC Usage
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// SHA-256 HMAC
|
|
171
|
-
const key = 'secret_key'
|
|
172
|
-
const message = 'Message to authenticate'
|
|
173
|
-
|
|
174
|
-
// Method 1: Using HMAC class
|
|
175
|
-
const hmacHasher = new Hash.SHA256HMAC(key)
|
|
176
|
-
hmacHasher.update(message)
|
|
177
|
-
const hmacResult = hmacHasher.digestHex()
|
|
178
|
-
console.log('HMAC-SHA256:', hmacResult)
|
|
179
|
-
// Output: b4d897472c73a052733d0796a5f71cf8253bab7d3969811b64f41ff6aa89d86f
|
|
180
|
-
|
|
181
|
-
// Method 2: Using helper function
|
|
182
|
-
const hmacHelper = Hash.sha256hmac(key, message)
|
|
183
|
-
console.log('HMAC helper result:', Utils.toHex(hmacHelper))
|
|
184
|
-
|
|
185
|
-
// Both methods produce the same result
|
|
186
|
-
console.log('HMAC results match:', hmacResult === Utils.toHex(hmacHelper))
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### HMAC with Different Algorithms
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
// SHA-512 HMAC
|
|
193
|
-
const sha512Hmac = new Hash.SHA512HMAC('my_secret_key')
|
|
194
|
-
sha512Hmac.update('Data to authenticate')
|
|
195
|
-
const sha512HmacResult = sha512Hmac.digestHex()
|
|
196
|
-
console.log('HMAC-SHA512:', sha512HmacResult)
|
|
197
|
-
|
|
198
|
-
// SHA-1 HMAC (legacy)
|
|
199
|
-
const sha1Hmac = new Hash.SHA1HMAC('legacy_key')
|
|
200
|
-
sha1Hmac.update('Legacy data')
|
|
201
|
-
const sha1HmacResult = sha1Hmac.digestHex()
|
|
202
|
-
console.log('HMAC-SHA1:', sha1HmacResult)
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### HMAC Key Management
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
// Strong key generation
|
|
209
|
-
function generateHmacKey(): string {
|
|
210
|
-
const randomBytes = new Array(32)
|
|
211
|
-
for (let i = 0; i < 32; i++) {
|
|
212
|
-
randomBytes[i] = Math.floor(Math.random() * 256)
|
|
213
|
-
}
|
|
214
|
-
return Utils.toHex(randomBytes)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Key derivation from password
|
|
218
|
-
function deriveKeyFromPassword(password: string, salt: string): number[] {
|
|
219
|
-
const combined = password + salt
|
|
220
|
-
return Hash.sha256(Utils.toArray(combined, 'utf8'))
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Example usage
|
|
224
|
-
const strongKey = generateHmacKey()
|
|
225
|
-
const derivedKey = deriveKeyFromPassword('user_password', 'random_salt')
|
|
226
|
-
|
|
227
|
-
console.log('Strong key:', strongKey)
|
|
228
|
-
console.log('Derived key:', Utils.toHex(derivedKey))
|
|
229
|
-
|
|
230
|
-
// Use derived key for HMAC
|
|
231
|
-
const secureHmac = Hash.sha256hmac(derivedKey, 'sensitive_data')
|
|
232
|
-
console.log('Secure HMAC:', Utils.toHex(secureHmac))
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Practical Applications
|
|
236
|
-
|
|
237
|
-
### Data Integrity Verification
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
class DataIntegrityChecker {
|
|
241
|
-
private data: string
|
|
242
|
-
private hash: string
|
|
243
|
-
|
|
244
|
-
constructor(data: string) {
|
|
245
|
-
this.data = data
|
|
246
|
-
this.hash = this.calculateHash(data)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private calculateHash(data: string): string {
|
|
250
|
-
const hashResult = Hash.sha256(Utils.toArray(data, 'utf8'))
|
|
251
|
-
return Utils.toHex(hashResult)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
verify(): boolean {
|
|
255
|
-
const currentHash = this.calculateHash(this.data)
|
|
256
|
-
return currentHash === this.hash
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
getData(): string {
|
|
260
|
-
return this.data
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
getHash(): string {
|
|
264
|
-
return this.hash
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Simulate data corruption
|
|
268
|
-
corruptData(): void {
|
|
269
|
-
this.data += '_corrupted'
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Example usage
|
|
274
|
-
const checker = new DataIntegrityChecker('Important document content')
|
|
275
|
-
console.log('Original hash:', checker.getHash())
|
|
276
|
-
console.log('Data is valid:', checker.verify()) // true
|
|
277
|
-
|
|
278
|
-
checker.corruptData()
|
|
279
|
-
console.log('After corruption, data is valid:', checker.verify()) // false
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### Message Authentication System
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
class MessageAuthenticator {
|
|
286
|
-
private secretKey: string
|
|
287
|
-
|
|
288
|
-
constructor(secretKey: string) {
|
|
289
|
-
this.secretKey = secretKey
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
createAuthenticatedMessage(message: string): {
|
|
293
|
-
message: string
|
|
294
|
-
hmac: string
|
|
295
|
-
timestamp: number
|
|
296
|
-
} {
|
|
297
|
-
const timestamp = Date.now()
|
|
298
|
-
const messageWithTimestamp = `${message}:${timestamp}`
|
|
299
|
-
const hmac = Hash.sha256hmac(this.secretKey, messageWithTimestamp)
|
|
300
|
-
|
|
301
|
-
return {
|
|
302
|
-
message,
|
|
303
|
-
hmac: Utils.toHex(hmac),
|
|
304
|
-
timestamp
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
verifyMessage(authenticatedMessage: {
|
|
309
|
-
message: string
|
|
310
|
-
hmac: string
|
|
311
|
-
timestamp: number
|
|
312
|
-
}): boolean {
|
|
313
|
-
try {
|
|
314
|
-
const messageWithTimestamp = `${authenticatedMessage.message}:${authenticatedMessage.timestamp}`
|
|
315
|
-
const expectedHmac = Hash.sha256hmac(this.secretKey, messageWithTimestamp)
|
|
316
|
-
const expectedHmacHex = Utils.toHex(expectedHmac)
|
|
317
|
-
|
|
318
|
-
// Constant-time comparison to prevent timing attacks
|
|
319
|
-
return this.constantTimeCompare(authenticatedMessage.hmac, expectedHmacHex)
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.error('Verification error:', error)
|
|
322
|
-
return false
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
private constantTimeCompare(a: string, b: string): boolean {
|
|
327
|
-
if (a.length !== b.length) {
|
|
328
|
-
return false
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
let result = 0
|
|
332
|
-
for (let i = 0; i < a.length; i++) {
|
|
333
|
-
result |= a.charCodeAt(i) ^ b.charCodeAt(i)
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return result === 0
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Example usage
|
|
341
|
-
const authenticator = new MessageAuthenticator('super_secret_key_123')
|
|
342
|
-
|
|
343
|
-
const authMessage = authenticator.createAuthenticatedMessage('Transfer 100 satoshis to Alice')
|
|
344
|
-
console.log('Authenticated message:', authMessage)
|
|
345
|
-
|
|
346
|
-
const isValid = authenticator.verifyMessage(authMessage)
|
|
347
|
-
console.log('Message is authentic:', isValid) // true
|
|
348
|
-
|
|
349
|
-
// Tamper with the message
|
|
350
|
-
authMessage.message = 'Transfer 1000 satoshis to Alice'
|
|
351
|
-
const isTamperedValid = authenticator.verifyMessage(authMessage)
|
|
352
|
-
console.log('Tampered message is authentic:', isTamperedValid) // false
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### Transaction Metadata Protection
|
|
356
|
-
|
|
357
|
-
```typescript
|
|
358
|
-
interface TransactionMetadata {
|
|
359
|
-
description: string
|
|
360
|
-
category: string
|
|
361
|
-
tags: string[]
|
|
362
|
-
amount: number
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
class SecureTransactionMetadata {
|
|
366
|
-
private key: number[]
|
|
367
|
-
|
|
368
|
-
constructor(password: string) {
|
|
369
|
-
// Derive key from password
|
|
370
|
-
this.key = Hash.sha256(Utils.toArray(password, 'utf8'))
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
protectMetadata(metadata: TransactionMetadata): {
|
|
374
|
-
data: string
|
|
375
|
-
integrity: string
|
|
376
|
-
} {
|
|
377
|
-
const jsonData = JSON.stringify(metadata)
|
|
378
|
-
const dataBytes = Utils.toArray(jsonData, 'utf8')
|
|
379
|
-
|
|
380
|
-
// Create integrity hash
|
|
381
|
-
const integrity = Hash.sha256hmac(this.key, dataBytes)
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
data: Utils.toBase64(dataBytes),
|
|
385
|
-
integrity: Utils.toHex(integrity)
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
verifyAndExtract(protectedData: {
|
|
390
|
-
data: string
|
|
391
|
-
integrity: string
|
|
392
|
-
}): TransactionMetadata | null {
|
|
393
|
-
try {
|
|
394
|
-
const dataBytes = Array.from(Buffer.from(protectedData.data, 'base64'))
|
|
395
|
-
const expectedIntegrity = Hash.sha256hmac(this.key, dataBytes)
|
|
396
|
-
const expectedIntegrityHex = Utils.toHex(expectedIntegrity)
|
|
397
|
-
|
|
398
|
-
if (protectedData.integrity !== expectedIntegrityHex) {
|
|
399
|
-
console.error('Integrity check failed')
|
|
400
|
-
return null
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const jsonString = Utils.toUTF8(dataBytes)
|
|
404
|
-
return JSON.parse(jsonString) as TransactionMetadata
|
|
405
|
-
} catch (error) {
|
|
406
|
-
console.error('Extraction error:', error)
|
|
407
|
-
return null
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Example usage
|
|
413
|
-
const metadataProtector = new SecureTransactionMetadata('user_password_123')
|
|
414
|
-
|
|
415
|
-
const originalMetadata: TransactionMetadata = {
|
|
416
|
-
description: 'Payment for services',
|
|
417
|
-
category: 'business',
|
|
418
|
-
tags: ['consulting', 'development'],
|
|
419
|
-
amount: 100
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const protectedData = metadataProtector.protectMetadata(originalMetadata)
|
|
423
|
-
console.log('Protected metadata:', protectedData)
|
|
424
|
-
|
|
425
|
-
const extracted = metadataProtector.verifyAndExtract(protectedData)
|
|
426
|
-
console.log('Extracted metadata:', extracted)
|
|
427
|
-
console.log('Metadata matches:', JSON.stringify(originalMetadata) === JSON.stringify(extracted))
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
## Performance Optimization
|
|
431
|
-
|
|
432
|
-
### Batch Hashing
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
class BatchHashProcessor {
|
|
436
|
-
private hasher: Hash.SHA256
|
|
437
|
-
|
|
438
|
-
constructor() {
|
|
439
|
-
this.hasher = new Hash.SHA256()
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
hashMultipleMessages(messages: string[]): string[] {
|
|
443
|
-
const results: string[] = []
|
|
444
|
-
|
|
445
|
-
for (const message of messages) {
|
|
446
|
-
// Reset hasher for each message
|
|
447
|
-
this.hasher = new Hash.SHA256()
|
|
448
|
-
this.hasher.update(message)
|
|
449
|
-
results.push(this.hasher.digestHex())
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
return results
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
createMerkleRoot(hashes: string[]): string {
|
|
456
|
-
if (hashes.length === 0) {
|
|
457
|
-
throw new Error('Cannot create merkle root from empty array')
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (hashes.length === 1) {
|
|
461
|
-
return hashes[0]
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const nextLevel: string[] = []
|
|
465
|
-
|
|
466
|
-
for (let i = 0; i < hashes.length; i += 2) {
|
|
467
|
-
const left = hashes[i]
|
|
468
|
-
const right = i + 1 < hashes.length ? hashes[i + 1] : left
|
|
469
|
-
|
|
470
|
-
const combined = left + right
|
|
471
|
-
const combinedBytes = Utils.toArray(combined, 'hex')
|
|
472
|
-
const hash = Hash.sha256(combinedBytes)
|
|
473
|
-
nextLevel.push(Utils.toHex(hash))
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return this.createMerkleRoot(nextLevel)
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Performance testing
|
|
481
|
-
function performanceTest() {
|
|
482
|
-
const processor = new BatchHashProcessor()
|
|
483
|
-
const testMessages = Array.from({ length: 1000 }, (_, i) => `Message ${i}`)
|
|
484
|
-
|
|
485
|
-
console.time('Batch hashing 1000 messages')
|
|
486
|
-
const hashes = processor.hashMultipleMessages(testMessages)
|
|
487
|
-
console.timeEnd('Batch hashing 1000 messages')
|
|
488
|
-
|
|
489
|
-
console.time('Creating merkle root')
|
|
490
|
-
const merkleRoot = processor.createMerkleRoot(hashes)
|
|
491
|
-
console.timeEnd('Creating merkle root')
|
|
492
|
-
|
|
493
|
-
console.log('Merkle root:', merkleRoot)
|
|
494
|
-
console.log('Processed', hashes.length, 'messages')
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
performanceTest()
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
### Memory-Efficient Streaming
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
class StreamingHasher {
|
|
504
|
-
private hasher: Hash.SHA256
|
|
505
|
-
|
|
506
|
-
constructor() {
|
|
507
|
-
this.hasher = new Hash.SHA256()
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
processLargeData(data: string, chunkSize: number = 1024): string {
|
|
511
|
-
for (let i = 0; i < data.length; i += chunkSize) {
|
|
512
|
-
const chunk = data.slice(i, i + chunkSize)
|
|
513
|
-
this.hasher.update(chunk)
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
return this.hasher.digestHex()
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
reset(): void {
|
|
520
|
-
this.hasher = new Hash.SHA256()
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Example with large data
|
|
525
|
-
const streamingHasher = new StreamingHasher()
|
|
526
|
-
const largeData = 'A'.repeat(1000000) // 1MB of data
|
|
527
|
-
const streamHash = streamingHasher.processLargeData(largeData)
|
|
528
|
-
console.log('Streaming hash result:', streamHash)
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
## Security Best Practices
|
|
532
|
-
|
|
533
|
-
### Secure Key Generation and Storage
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
class SecureKeyManager {
|
|
537
|
-
static generateSecureKey(length: number = 32): number[] {
|
|
538
|
-
// In production, use a cryptographically secure random number generator
|
|
539
|
-
const key = new Array(length)
|
|
540
|
-
for (let i = 0; i < length; i++) {
|
|
541
|
-
key[i] = Math.floor(Math.random() * 256)
|
|
542
|
-
}
|
|
543
|
-
return key
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
static deriveKeyFromPassword(
|
|
547
|
-
password: string,
|
|
548
|
-
salt: number[],
|
|
549
|
-
iterations: number = 10000
|
|
550
|
-
): number[] {
|
|
551
|
-
let derived = Hash.sha256(Utils.toArray(password + Utils.toHex(salt), 'utf8'))
|
|
552
|
-
|
|
553
|
-
for (let i = 1; i < iterations; i++) {
|
|
554
|
-
derived = Hash.sha256(derived)
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
return derived
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
static secureCompare(a: number[], b: number[]): boolean {
|
|
561
|
-
if (a.length !== b.length) {
|
|
562
|
-
return false
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
let result = 0
|
|
566
|
-
for (let i = 0; i < a.length; i++) {
|
|
567
|
-
result |= a[i] ^ b[i]
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
return result === 0
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
static clearSensitiveData(data: number[]): void {
|
|
574
|
-
for (let i = 0; i < data.length; i++) {
|
|
575
|
-
data[i] = 0
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Example secure usage
|
|
581
|
-
const salt = SecureKeyManager.generateSecureKey(16)
|
|
582
|
-
const derivedKey = SecureKeyManager.deriveKeyFromPassword('user_password', salt)
|
|
583
|
-
|
|
584
|
-
console.log('Salt:', Utils.toHex(salt))
|
|
585
|
-
console.log('Derived key:', Utils.toHex(derivedKey))
|
|
586
|
-
|
|
587
|
-
// Clear sensitive data when done
|
|
588
|
-
SecureKeyManager.clearSensitiveData(derivedKey)
|
|
589
|
-
SecureKeyManager.clearSensitiveData(salt)
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
### Input Validation and Error Handling
|
|
593
|
-
|
|
594
|
-
```typescript
|
|
595
|
-
class SafeHasher {
|
|
596
|
-
static validateInput(input: any): void {
|
|
597
|
-
if (input === null || input === undefined) {
|
|
598
|
-
throw new Error('Input cannot be null or undefined')
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
if (typeof input === 'string' && input.length === 0) {
|
|
602
|
-
throw new Error('Input string cannot be empty')
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
if (Array.isArray(input) && input.length === 0) {
|
|
606
|
-
throw new Error('Input array cannot be empty')
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
static safeHash(input: string | number[], algorithm: 'sha256' | 'sha512' = 'sha256'): string {
|
|
611
|
-
try {
|
|
612
|
-
this.validateInput(input)
|
|
613
|
-
|
|
614
|
-
let data: number[]
|
|
615
|
-
if (typeof input === 'string') {
|
|
616
|
-
data = Utils.toArray(input, 'utf8')
|
|
617
|
-
} else {
|
|
618
|
-
data = input
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
let result: number[]
|
|
622
|
-
switch (algorithm) {
|
|
623
|
-
case 'sha256':
|
|
624
|
-
result = Hash.sha256(data)
|
|
625
|
-
break
|
|
626
|
-
case 'sha512':
|
|
627
|
-
result = Hash.sha512(data)
|
|
628
|
-
break
|
|
629
|
-
default:
|
|
630
|
-
throw new Error(`Unsupported algorithm: ${algorithm}`)
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
return Utils.toHex(result)
|
|
634
|
-
} catch (error) {
|
|
635
|
-
console.error('Hashing error:', error)
|
|
636
|
-
throw new Error(`Failed to hash input: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
static safeHmac(key: string | number[], message: string | number[]): string {
|
|
641
|
-
try {
|
|
642
|
-
this.validateInput(key)
|
|
643
|
-
this.validateInput(message)
|
|
644
|
-
|
|
645
|
-
const result = Hash.sha256hmac(key, message)
|
|
646
|
-
return Utils.toHex(result)
|
|
647
|
-
} catch (error) {
|
|
648
|
-
console.error('HMAC error:', error)
|
|
649
|
-
throw new Error(`Failed to create HMAC: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Example safe usage
|
|
655
|
-
try {
|
|
656
|
-
const hash = SafeHasher.safeHash('Valid input')
|
|
657
|
-
console.log('Safe hash:', hash)
|
|
658
|
-
|
|
659
|
-
const hmac = SafeHasher.safeHmac('secret_key', 'message')
|
|
660
|
-
console.log('Safe HMAC:', hmac)
|
|
661
|
-
} catch (error) {
|
|
662
|
-
console.error('Operation failed:', error)
|
|
663
|
-
}
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
## Testing Your Implementation
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
// Comprehensive test suite
|
|
670
|
-
function runHashTests(): void {
|
|
671
|
-
console.log('Running hash function tests...')
|
|
672
|
-
|
|
673
|
-
// Test SHA-256 consistency
|
|
674
|
-
const testMessage = 'Hello, Bitcoin!'
|
|
675
|
-
const hash1 = Hash.sha256(Utils.toArray(testMessage, 'utf8'))
|
|
676
|
-
const hash2 = new Hash.SHA256().update(testMessage).digest()
|
|
677
|
-
|
|
678
|
-
console.assert(
|
|
679
|
-
Utils.toHex(hash1) === Utils.toHex(hash2),
|
|
680
|
-
'SHA-256 methods should produce same result'
|
|
681
|
-
)
|
|
682
|
-
|
|
683
|
-
// Test HMAC consistency
|
|
684
|
-
const key = 'test_key'
|
|
685
|
-
const message = 'test_message'
|
|
686
|
-
const hmac1 = Hash.sha256hmac(key, message)
|
|
687
|
-
const hmac2 = new Hash.SHA256HMAC(key).update(message).digest()
|
|
688
|
-
|
|
689
|
-
console.assert(
|
|
690
|
-
Utils.toHex(hmac1) === Utils.toHex(hmac2),
|
|
691
|
-
'HMAC methods should produce same result'
|
|
692
|
-
)
|
|
693
|
-
|
|
694
|
-
// Test Bitcoin-specific functions
|
|
695
|
-
const data = 'bitcoin_data'
|
|
696
|
-
const hash256Result = Hash.hash256(Utils.toArray(data, 'utf8'))
|
|
697
|
-
const manualDouble = Hash.sha256(Hash.sha256(Utils.toArray(data, 'utf8')))
|
|
698
|
-
|
|
699
|
-
console.assert(
|
|
700
|
-
Utils.toHex(hash256Result) === Utils.toHex(manualDouble),
|
|
701
|
-
'hash256 should equal double SHA-256'
|
|
702
|
-
)
|
|
703
|
-
|
|
704
|
-
console.log('All tests passed!')
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
runHashTests()
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
## Troubleshooting Common Issues
|
|
711
|
-
|
|
712
|
-
### Issue 1: Encoding Problems
|
|
713
|
-
|
|
714
|
-
```typescript
|
|
715
|
-
// Problem: Incorrect encoding leads to different hashes
|
|
716
|
-
// Note: Hash.sha256() requires number[] input, not strings directly
|
|
717
|
-
const correctHash = Hash.sha256(Utils.toArray('hello', 'utf8')) // Correct approach
|
|
718
|
-
|
|
719
|
-
console.log('Correct hash with proper encoding:', Utils.toHex(correctHash))
|
|
720
|
-
console.log('Always use Utils.toArray() for proper encoding')
|
|
721
|
-
```
|
|
722
|
-
|
|
723
|
-
### Issue 2: Key Management
|
|
724
|
-
|
|
725
|
-
```typescript
|
|
726
|
-
// Problem: Weak keys or improper key derivation
|
|
727
|
-
const weakKey = 'password123' // Weak
|
|
728
|
-
const strongKey = SecureKeyManager.generateSecureKey() // Strong
|
|
729
|
-
|
|
730
|
-
// Problem: Reusing keys across different purposes
|
|
731
|
-
const hmacKey = strongKey
|
|
732
|
-
const encryptionKey = strongKey // Wrong: same key for different purposes
|
|
733
|
-
|
|
734
|
-
// Solution: Derive different keys for different purposes
|
|
735
|
-
const hmacKeyDerived = Hash.sha256(Utils.toArray('hmac:' + Utils.toHex(strongKey), 'utf8'))
|
|
736
|
-
const encryptionKeyDerived = Hash.sha256(Utils.toArray('encrypt:' + Utils.toHex(strongKey), 'utf8'))
|
|
737
|
-
```
|
|
738
|
-
|
|
739
|
-
### Issue 3: Performance Problems
|
|
740
|
-
|
|
741
|
-
```typescript
|
|
742
|
-
// Problem: Creating new hasher instances unnecessarily
|
|
743
|
-
function inefficientHashing(messages: string[]): string[] {
|
|
744
|
-
return messages.map(msg => {
|
|
745
|
-
const hasher = new Hash.SHA256() // Inefficient: new instance each time
|
|
746
|
-
return hasher.update(msg).digestHex()
|
|
747
|
-
})
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Solution: Reuse hasher or use helper functions
|
|
751
|
-
function efficientHashing(messages: string[]): string[] {
|
|
752
|
-
return messages.map(msg => {
|
|
753
|
-
return Utils.toHex(Hash.sha256(Utils.toArray(msg, 'utf8'))) // Efficient
|
|
754
|
-
})
|
|
755
|
-
}
|
|
756
|
-
```
|
|
757
|
-
|
|
758
|
-
## Summary
|
|
759
|
-
|
|
760
|
-
This tutorial covered comprehensive usage of cryptographic hashing and HMACs in the BSV TypeScript SDK:
|
|
761
|
-
|
|
762
|
-
**Key Concepts Learned:**
|
|
763
|
-
|
|
764
|
-
- Hash function fundamentals and Bitcoin-specific applications
|
|
765
|
-
- SHA-256, SHA-512, SHA-1, and RIPEMD-160 implementation
|
|
766
|
-
- HMAC creation and verification for message authentication
|
|
767
|
-
- Bitcoin-specific functions: hash256 (double SHA-256) and hash160
|
|
768
|
-
- Performance optimization techniques for batch processing
|
|
769
|
-
- Security best practices for key management and validation
|
|
770
|
-
|
|
771
|
-
**Practical Applications:**
|
|
772
|
-
|
|
773
|
-
- Data integrity verification systems
|
|
774
|
-
- Message authentication protocols
|
|
775
|
-
- Transaction metadata protection
|
|
776
|
-
- Merkle tree construction
|
|
777
|
-
- Secure key derivation patterns
|
|
778
|
-
|
|
779
|
-
**Security Considerations:**
|
|
780
|
-
|
|
781
|
-
- Proper input validation and error handling
|
|
782
|
-
- Constant-time comparison to prevent timing attacks
|
|
783
|
-
- Secure key generation and storage practices
|
|
784
|
-
- Memory management for sensitive data
|
|
785
|
-
|
|
786
|
-
The Hash module in the BSV TypeScript SDK provides both low-level control through classes and high-level convenience through helper functions, enabling developers to implement robust cryptographic solutions for Bitcoin applications.
|
|
787
|
-
|
|
788
|
-
Continue exploring advanced cryptographic topics with the [ECDH Key Exchange](./ecdh-key-exchange.md) and [AES Symmetric Encryption](./aes-encryption.md) tutorials to build complete cryptographic systems.
|