@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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/kvstore/GlobalKVStore.js +116 -98
- package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/cjs/src/kvstore/types.js.map +1 -1
- package/dist/cjs/src/overlay-tools/index.js +1 -0
- package/dist/cjs/src/overlay-tools/index.js.map +1 -1
- package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js +55 -0
- package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/kvstore/GlobalKVStore.js +117 -99
- package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/esm/src/kvstore/types.js.map +1 -1
- package/dist/esm/src/overlay-tools/index.js +1 -0
- package/dist/esm/src/overlay-tools/index.js.map +1 -1
- package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js +48 -0
- package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
- package/dist/types/src/kvstore/types.d.ts +2 -0
- package/dist/types/src/kvstore/types.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/index.d.ts +1 -0
- package/dist/types/src/overlay-tools/index.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts +14 -0
- package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +2 -2
- package/dist/umd/bundle.js.map +1 -1
- package/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/reference/kvstore.md +9 -0
- package/docs/reference/overlay-tools.md +32 -0
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- package/src/kvstore/GlobalKVStore.ts +134 -114
- package/src/kvstore/__tests/GlobalKVStore.test.ts +11 -1
- package/src/kvstore/types.ts +2 -0
- package/src/overlay-tools/index.ts +1 -0
- package/src/overlay-tools/withDoubleSpendRetry.ts +71 -0
- 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,549 +0,0 @@
|
|
|
1
|
-
# ECDH Key Exchange
|
|
2
|
-
|
|
3
|
-
**Duration**: 75 minutes
|
|
4
|
-
**Prerequisites**: Basic TypeScript knowledge, [Elliptic Curve Fundamentals](./elliptic-curve-fundamentals.md) tutorial completed
|
|
5
|
-
|
|
6
|
-
## Learning Goals
|
|
7
|
-
|
|
8
|
-
- Understand Elliptic Curve Diffie-Hellman (ECDH) key exchange principles
|
|
9
|
-
- Implement secure key exchange using the BSV TypeScript SDK
|
|
10
|
-
- Create shared secrets for encrypted communication
|
|
11
|
-
- Apply ECDH in practical Bitcoin applications
|
|
12
|
-
- Understand security considerations and best practices
|
|
13
|
-
|
|
14
|
-
## Introduction to ECDH
|
|
15
|
-
|
|
16
|
-
Elliptic Curve Diffie-Hellman (ECDH) is a key agreement protocol that allows two parties to establish a shared secret over an unsecured communication channel. Unlike traditional encryption where you need to share a secret key beforehand, ECDH allows two parties who have never met to create a shared secret that only they know.
|
|
17
|
-
|
|
18
|
-
The mathematical foundation of ECDH relies on the commutative property of elliptic curve point multiplication:
|
|
19
|
-
|
|
20
|
-
- Alice computes: `(Alice's private key) × (Bob's public key)`
|
|
21
|
-
- Bob computes: `(Bob's private key) × (Alice's public key)`
|
|
22
|
-
- Both arrive at the same shared secret point
|
|
23
|
-
|
|
24
|
-
## Setting Up Your Environment
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import { PrivateKey, PublicKey, Point, BigNumber } from '@bsv/sdk'
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Basic ECDH Key Exchange
|
|
31
|
-
|
|
32
|
-
### Step 1: Key Generation
|
|
33
|
-
|
|
34
|
-
Let's start by generating key pairs for Alice and Bob:
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
function generateKeyPairs() {
|
|
38
|
-
console.log('=== Generating Key Pairs ===')
|
|
39
|
-
|
|
40
|
-
// Generate Alice's key pair
|
|
41
|
-
const alicePrivKey = PrivateKey.fromRandom()
|
|
42
|
-
const alicePubKey = alicePrivKey.toPublicKey()
|
|
43
|
-
|
|
44
|
-
// Generate Bob's key pair
|
|
45
|
-
const bobPrivKey = PrivateKey.fromRandom()
|
|
46
|
-
const bobPubKey = bobPrivKey.toPublicKey()
|
|
47
|
-
|
|
48
|
-
console.log('Alice private key:', alicePrivKey.toWif())
|
|
49
|
-
console.log('Alice public key:', alicePubKey.toString())
|
|
50
|
-
console.log('Bob private key:', bobPrivKey.toWif())
|
|
51
|
-
console.log('Bob public key:', bobPubKey.toString())
|
|
52
|
-
|
|
53
|
-
return { alicePrivKey, alicePubKey, bobPrivKey, bobPubKey }
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### Step 2: Deriving Shared Secrets
|
|
58
|
-
|
|
59
|
-
Now both parties can derive the same shared secret:
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
function performECDH(alicePrivKey: PrivateKey, alicePubKey: PublicKey,
|
|
63
|
-
bobPrivKey: PrivateKey, bobPubKey: PublicKey) {
|
|
64
|
-
console.log('\n=== ECDH Key Exchange ===')
|
|
65
|
-
|
|
66
|
-
// Alice creates a shared secret using Bob's public key and her private key
|
|
67
|
-
const aliceSharedSecret = alicePrivKey.deriveSharedSecret(bobPubKey)
|
|
68
|
-
|
|
69
|
-
// Bob creates the same shared secret using Alice's public key and his private key
|
|
70
|
-
const bobSharedSecret = bobPrivKey.deriveSharedSecret(alicePubKey)
|
|
71
|
-
|
|
72
|
-
// Verify they're identical
|
|
73
|
-
const aliceSecretHex = aliceSharedSecret.getX().toHex()
|
|
74
|
-
const bobSecretHex = bobSharedSecret.getX().toHex()
|
|
75
|
-
|
|
76
|
-
console.log('Alice\'s shared secret (x-coordinate):', aliceSecretHex)
|
|
77
|
-
console.log('Bob\'s shared secret (x-coordinate):', bobSecretHex)
|
|
78
|
-
console.log('Secrets match:', aliceSecretHex === bobSecretHex)
|
|
79
|
-
|
|
80
|
-
return aliceSharedSecret
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Step 3: Complete Example
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
function basicECDHExample() {
|
|
88
|
-
try {
|
|
89
|
-
// Generate key pairs
|
|
90
|
-
const { alicePrivKey, alicePubKey, bobPrivKey, bobPubKey } = generateKeyPairs()
|
|
91
|
-
|
|
92
|
-
// Perform ECDH
|
|
93
|
-
const sharedSecret = performECDH(alicePrivKey, alicePubKey, bobPrivKey, bobPubKey)
|
|
94
|
-
|
|
95
|
-
// The shared secret is a point on the curve
|
|
96
|
-
console.log('\nShared secret point:')
|
|
97
|
-
console.log('X:', sharedSecret.getX().toHex())
|
|
98
|
-
console.log('Y:', sharedSecret.getY().toHex())
|
|
99
|
-
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error('ECDH Error:', error.message)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Run the example
|
|
106
|
-
basicECDHExample()
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Security Validation
|
|
110
|
-
|
|
111
|
-
The SDK includes built-in security checks to prevent twist attacks:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
function demonstrateSecurityValidation() {
|
|
115
|
-
console.log('\n=== Security Validation ===')
|
|
116
|
-
|
|
117
|
-
const validPrivKey = PrivateKey.fromRandom()
|
|
118
|
-
|
|
119
|
-
// This will work - valid public key
|
|
120
|
-
const validPubKey = PrivateKey.fromRandom().toPublicKey()
|
|
121
|
-
const validSecret = validPrivKey.deriveSharedSecret(validPubKey)
|
|
122
|
-
console.log('Valid ECDH succeeded')
|
|
123
|
-
|
|
124
|
-
// This will fail - invalid point (not on curve)
|
|
125
|
-
try {
|
|
126
|
-
const invalidPubKey = new PublicKey(new BigNumber(14), new BigNumber(16))
|
|
127
|
-
validPrivKey.deriveSharedSecret(invalidPubKey)
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.log('Security check prevented invalid key usage:', error.message)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
demonstrateSecurityValidation()
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Practical Applications
|
|
137
|
-
|
|
138
|
-
### Secure Message Exchange
|
|
139
|
-
|
|
140
|
-
Here's how to use ECDH for encrypting messages:
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { createHash, createCipheriv, createDecipheriv, randomBytes } from 'crypto'
|
|
144
|
-
|
|
145
|
-
function deriveEncryptionKey(sharedSecret: Point): Buffer {
|
|
146
|
-
// Use the x-coordinate of the shared secret as key material
|
|
147
|
-
const keyMaterial = sharedSecret.getX().toArray('be', 32)
|
|
148
|
-
|
|
149
|
-
// Hash to create a proper encryption key
|
|
150
|
-
return createHash('sha256').update(Buffer.from(keyMaterial)).digest()
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function encryptMessage(message: string, sharedSecret: Point): {
|
|
154
|
-
encrypted: string,
|
|
155
|
-
iv: string
|
|
156
|
-
} {
|
|
157
|
-
const key = deriveEncryptionKey(sharedSecret)
|
|
158
|
-
const iv = randomBytes(16)
|
|
159
|
-
const cipher = createCipheriv('aes-256-cbc', key, iv)
|
|
160
|
-
|
|
161
|
-
let encrypted = cipher.update(message, 'utf8', 'hex')
|
|
162
|
-
encrypted += cipher.final('hex')
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
encrypted,
|
|
166
|
-
iv: iv.toString('hex')
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function decryptMessage(encryptedData: { encrypted: string, iv: string },
|
|
171
|
-
sharedSecret: Point): string {
|
|
172
|
-
const key = deriveEncryptionKey(sharedSecret)
|
|
173
|
-
const decipher = createDecipheriv('aes-256-cbc', key, Buffer.from(encryptedData.iv, 'hex'))
|
|
174
|
-
|
|
175
|
-
let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8')
|
|
176
|
-
decrypted += decipher.final('utf8')
|
|
177
|
-
|
|
178
|
-
return decrypted
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function secureMessagingExample() {
|
|
182
|
-
console.log('\n=== Secure Messaging with ECDH ===')
|
|
183
|
-
|
|
184
|
-
// Setup key pairs
|
|
185
|
-
const alicePrivKey = PrivateKey.fromRandom()
|
|
186
|
-
const bobPrivKey = PrivateKey.fromRandom()
|
|
187
|
-
const alicePubKey = alicePrivKey.toPublicKey()
|
|
188
|
-
const bobPubKey = bobPrivKey.toPublicKey()
|
|
189
|
-
|
|
190
|
-
// Derive shared secret
|
|
191
|
-
const sharedSecret = alicePrivKey.deriveSharedSecret(bobPubKey)
|
|
192
|
-
|
|
193
|
-
// Alice encrypts a message
|
|
194
|
-
const message = "Hello Bob! This is a secret message."
|
|
195
|
-
const encryptedData = encryptMessage(message, sharedSecret)
|
|
196
|
-
|
|
197
|
-
console.log('Original message:', message)
|
|
198
|
-
console.log('Encrypted:', encryptedData.encrypted)
|
|
199
|
-
|
|
200
|
-
// Bob decrypts the message using the same shared secret
|
|
201
|
-
const bobSharedSecret = bobPrivKey.deriveSharedSecret(alicePubKey)
|
|
202
|
-
const decryptedMessage = decryptMessage(encryptedData, bobSharedSecret)
|
|
203
|
-
|
|
204
|
-
console.log('Decrypted message:', decryptedMessage)
|
|
205
|
-
console.log('Messages match:', message === decryptedMessage)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
secureMessagingExample()
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Key Exchange with Authentication
|
|
212
|
-
|
|
213
|
-
Combine ECDH with digital signatures for authenticated key exchange:
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
function authenticatedKeyExchange() {
|
|
217
|
-
console.log('\n=== Authenticated Key Exchange ===')
|
|
218
|
-
|
|
219
|
-
// Generate long-term identity keys
|
|
220
|
-
const aliceIdentityPrivKey = PrivateKey.fromRandom()
|
|
221
|
-
const bobIdentityPrivKey = PrivateKey.fromRandom()
|
|
222
|
-
const aliceIdentityPubKey = aliceIdentityPrivKey.toPublicKey()
|
|
223
|
-
const bobIdentityPubKey = bobIdentityPrivKey.toPublicKey()
|
|
224
|
-
|
|
225
|
-
// Generate ephemeral keys for this session
|
|
226
|
-
const aliceEphemeralPrivKey = PrivateKey.fromRandom()
|
|
227
|
-
const bobEphemeralPrivKey = PrivateKey.fromRandom()
|
|
228
|
-
const aliceEphemeralPubKey = aliceEphemeralPrivKey.toPublicKey()
|
|
229
|
-
const bobEphemeralPubKey = bobEphemeralPrivKey.toPublicKey()
|
|
230
|
-
|
|
231
|
-
// Alice signs her ephemeral public key with her identity key
|
|
232
|
-
const aliceSignature = aliceIdentityPrivKey.sign(
|
|
233
|
-
Buffer.from(aliceEphemeralPubKey.toString(), 'utf8')
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
// Bob signs his ephemeral public key with his identity key
|
|
237
|
-
const bobSignature = bobIdentityPrivKey.sign(
|
|
238
|
-
Buffer.from(bobEphemeralPubKey.toString(), 'utf8')
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
// Verify signatures (in practice, you'd exchange these over the network)
|
|
242
|
-
const aliceSignatureValid = aliceIdentityPubKey.verify(
|
|
243
|
-
Buffer.from(aliceEphemeralPubKey.toString(), 'utf8'),
|
|
244
|
-
aliceSignature
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
const bobSignatureValid = bobIdentityPubKey.verify(
|
|
248
|
-
Buffer.from(bobEphemeralPubKey.toString(), 'utf8'),
|
|
249
|
-
bobSignature
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
console.log('Alice signature valid:', aliceSignatureValid)
|
|
253
|
-
console.log('Bob signature valid:', bobSignatureValid)
|
|
254
|
-
|
|
255
|
-
if (aliceSignatureValid && bobSignatureValid) {
|
|
256
|
-
// Perform ECDH with ephemeral keys
|
|
257
|
-
const sharedSecret = aliceEphemeralPrivKey.deriveSharedSecret(bobEphemeralPubKey)
|
|
258
|
-
console.log('Authenticated shared secret established')
|
|
259
|
-
console.log('Secret (x-coordinate):', sharedSecret.getX().toHex().substring(0, 16) + '...')
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
authenticatedKeyExchange()
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Advanced ECDH Patterns
|
|
267
|
-
|
|
268
|
-
### Multi-Party Key Agreement
|
|
269
|
-
|
|
270
|
-
Extend ECDH to multiple parties:
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
function multiPartyKeyAgreement() {
|
|
274
|
-
console.log('\n=== Multi-Party Key Agreement ===')
|
|
275
|
-
|
|
276
|
-
// Generate keys for three parties
|
|
277
|
-
const parties = ['Alice', 'Bob', 'Charlie'].map(name => ({
|
|
278
|
-
name,
|
|
279
|
-
privKey: PrivateKey.fromRandom(),
|
|
280
|
-
pubKey: null as PublicKey | null
|
|
281
|
-
}))
|
|
282
|
-
|
|
283
|
-
// Generate public keys
|
|
284
|
-
parties.forEach(party => {
|
|
285
|
-
party.pubKey = party.privKey.toPublicKey()
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
// Each party computes pairwise shared secrets
|
|
289
|
-
const sharedSecrets = new Map<string, Point>()
|
|
290
|
-
|
|
291
|
-
for (let i = 0; i < parties.length; i++) {
|
|
292
|
-
for (let j = i + 1; j < parties.length; j++) {
|
|
293
|
-
const party1 = parties[i]
|
|
294
|
-
const party2 = parties[j]
|
|
295
|
-
|
|
296
|
-
const secret = party1.privKey.deriveSharedSecret(party2.pubKey!)
|
|
297
|
-
const pairKey = `${party1.name}-${party2.name}`
|
|
298
|
-
sharedSecrets.set(pairKey, secret)
|
|
299
|
-
|
|
300
|
-
console.log(`${pairKey} shared secret:`, secret.getX().toHex().substring(0, 16) + '...')
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return sharedSecrets
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
multiPartyKeyAgreement()
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Key Derivation Functions
|
|
311
|
-
|
|
312
|
-
Use proper key derivation for different purposes:
|
|
313
|
-
|
|
314
|
-
```typescript
|
|
315
|
-
function keyDerivationExample() {
|
|
316
|
-
console.log('\n=== Key Derivation Functions ===')
|
|
317
|
-
|
|
318
|
-
const alicePrivKey = PrivateKey.fromRandom()
|
|
319
|
-
const bobPrivKey = PrivateKey.fromRandom()
|
|
320
|
-
const sharedSecret = alicePrivKey.deriveSharedSecret(bobPrivKey.toPublicKey())
|
|
321
|
-
|
|
322
|
-
// Derive different keys for different purposes
|
|
323
|
-
function deriveKey(purpose: string, length: number = 32): Buffer {
|
|
324
|
-
const keyMaterial = sharedSecret.getX().toArray('be', 32)
|
|
325
|
-
const hash = createHash('sha256')
|
|
326
|
-
hash.update(Buffer.from(keyMaterial))
|
|
327
|
-
hash.update(Buffer.from(purpose, 'utf8'))
|
|
328
|
-
return hash.digest().slice(0, length)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const encryptionKey = deriveKey('encryption', 32)
|
|
332
|
-
const macKey = deriveKey('authentication', 32)
|
|
333
|
-
const ivKey = deriveKey('iv', 16)
|
|
334
|
-
|
|
335
|
-
console.log('Encryption key:', encryptionKey.toString('hex'))
|
|
336
|
-
console.log('MAC key:', macKey.toString('hex'))
|
|
337
|
-
console.log('IV key:', ivKey.toString('hex'))
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
keyDerivationExample()
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
## Security Considerations
|
|
344
|
-
|
|
345
|
-
### Best Practices
|
|
346
|
-
|
|
347
|
-
1. **Key Validation**: Always validate public keys before use
|
|
348
|
-
2. **Ephemeral Keys**: Use ephemeral keys for forward secrecy
|
|
349
|
-
3. **Authentication**: Combine with signatures to prevent man-in-the-middle attacks
|
|
350
|
-
4. **Key Derivation**: Use proper KDFs to derive encryption keys from shared secrets
|
|
351
|
-
|
|
352
|
-
### Common Pitfalls
|
|
353
|
-
|
|
354
|
-
```typescript
|
|
355
|
-
function securityPitfalls() {
|
|
356
|
-
console.log('\n=== Security Pitfalls to Avoid ===')
|
|
357
|
-
|
|
358
|
-
// ❌ DON'T: Use shared secret directly as encryption key
|
|
359
|
-
console.log('❌ Never use the shared secret point directly for encryption')
|
|
360
|
-
|
|
361
|
-
// ✅ DO: Use proper key derivation
|
|
362
|
-
console.log('✅ Always use key derivation functions')
|
|
363
|
-
|
|
364
|
-
// ❌ DON'T: Reuse ephemeral keys
|
|
365
|
-
console.log('❌ Never reuse ephemeral keys across sessions')
|
|
366
|
-
|
|
367
|
-
// ✅ DO: Generate fresh ephemeral keys for each session
|
|
368
|
-
console.log('✅ Generate fresh keys for each exchange')
|
|
369
|
-
|
|
370
|
-
// ❌ DON'T: Skip public key validation
|
|
371
|
-
console.log('❌ Never skip public key validation')
|
|
372
|
-
|
|
373
|
-
// ✅ DO: Always validate received public keys
|
|
374
|
-
console.log('✅ SDK automatically validates keys in deriveSharedSecret()')
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
securityPitfalls()
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
## Performance Considerations
|
|
381
|
-
|
|
382
|
-
### Optimizing ECDH Operations
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
function performanceExample() {
|
|
386
|
-
console.log('\n=== Performance Optimization ===')
|
|
387
|
-
|
|
388
|
-
const iterations = 1000
|
|
389
|
-
|
|
390
|
-
// Pre-generate keys
|
|
391
|
-
const privateKeys = Array.from({ length: iterations }, () => PrivateKey.fromRandom())
|
|
392
|
-
const publicKeys = privateKeys.map(pk => pk.toPublicKey())
|
|
393
|
-
|
|
394
|
-
// Measure ECDH performance
|
|
395
|
-
const startTime = Date.now()
|
|
396
|
-
|
|
397
|
-
for (let i = 0; i < iterations; i++) {
|
|
398
|
-
const sharedSecret = privateKeys[i].deriveSharedSecret(publicKeys[(i + 1) % iterations])
|
|
399
|
-
// In practice, you'd process the shared secret here
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const endTime = Date.now()
|
|
403
|
-
const avgTime = (endTime - startTime) / iterations
|
|
404
|
-
|
|
405
|
-
console.log(`Performed ${iterations} ECDH operations`)
|
|
406
|
-
console.log(`Average time per operation: ${avgTime.toFixed(2)}ms`)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
performanceExample()
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
## Error Handling
|
|
413
|
-
|
|
414
|
-
### Robust ECDH Implementation
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
function robustECDH(privateKey: PrivateKey, publicKey: PublicKey): Point | null {
|
|
418
|
-
try {
|
|
419
|
-
// Validate inputs
|
|
420
|
-
if (!privateKey || !publicKey) {
|
|
421
|
-
throw new Error('Invalid key parameters')
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Perform ECDH with built-in validation
|
|
425
|
-
const sharedSecret = privateKey.deriveSharedSecret(publicKey)
|
|
426
|
-
|
|
427
|
-
// Additional validation if needed
|
|
428
|
-
if (sharedSecret.getX().isZero() || sharedSecret.getY().isZero()) {
|
|
429
|
-
throw new Error('Invalid shared secret generated')
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return sharedSecret
|
|
433
|
-
|
|
434
|
-
} catch (error) {
|
|
435
|
-
console.error('ECDH operation failed:', error.message)
|
|
436
|
-
return null
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function errorHandlingExample() {
|
|
441
|
-
console.log('\n=== Error Handling ===')
|
|
442
|
-
|
|
443
|
-
const validPrivKey = PrivateKey.fromRandom()
|
|
444
|
-
const validPubKey = PrivateKey.fromRandom().toPublicKey()
|
|
445
|
-
|
|
446
|
-
// Test with valid keys
|
|
447
|
-
const result1 = robustECDH(validPrivKey, validPubKey)
|
|
448
|
-
console.log('Valid ECDH result:', result1 ? 'Success' : 'Failed')
|
|
449
|
-
|
|
450
|
-
// Test with invalid key (will be caught by SDK validation)
|
|
451
|
-
try {
|
|
452
|
-
const invalidPubKey = new PublicKey(new BigNumber(1), new BigNumber(1))
|
|
453
|
-
const result2 = robustECDH(validPrivKey, invalidPubKey)
|
|
454
|
-
console.log('Invalid ECDH result:', result2 ? 'Success' : 'Failed')
|
|
455
|
-
} catch (error) {
|
|
456
|
-
console.log('Caught invalid key error:', error.message)
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
errorHandlingExample()
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
## Testing Your ECDH Implementation
|
|
464
|
-
|
|
465
|
-
### Comprehensive Test Suite
|
|
466
|
-
|
|
467
|
-
```typescript
|
|
468
|
-
function testECDHImplementation() {
|
|
469
|
-
console.log('\n=== ECDH Test Suite ===')
|
|
470
|
-
|
|
471
|
-
let passed = 0
|
|
472
|
-
let total = 0
|
|
473
|
-
|
|
474
|
-
function test(name: string, testFn: () => boolean) {
|
|
475
|
-
total++
|
|
476
|
-
try {
|
|
477
|
-
if (testFn()) {
|
|
478
|
-
console.log(`✅ ${name}`)
|
|
479
|
-
passed++
|
|
480
|
-
} else {
|
|
481
|
-
console.log(`❌ ${name}`)
|
|
482
|
-
}
|
|
483
|
-
} catch (error) {
|
|
484
|
-
console.log(`❌ ${name}: ${error.message}`)
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Test 1: Basic ECDH symmetry
|
|
489
|
-
test('Basic ECDH symmetry', () => {
|
|
490
|
-
const privA = PrivateKey.fromRandom()
|
|
491
|
-
const privB = PrivateKey.fromRandom()
|
|
492
|
-
const secretA = privA.deriveSharedSecret(privB.toPublicKey())
|
|
493
|
-
const secretB = privB.deriveSharedSecret(privA.toPublicKey())
|
|
494
|
-
return secretA.getX().toHex() === secretB.getX().toHex()
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
// Test 2: Different key formats
|
|
498
|
-
test('Different key formats', () => {
|
|
499
|
-
const privA = PrivateKey.fromRandom()
|
|
500
|
-
const privB = PrivateKey.fromRandom()
|
|
501
|
-
const pubB = PublicKey.fromString(privB.toPublicKey().toDER('hex') as string)
|
|
502
|
-
const secret1 = privA.deriveSharedSecret(privB.toPublicKey())
|
|
503
|
-
const secret2 = privA.deriveSharedSecret(pubB)
|
|
504
|
-
return secret1.getX().toHex() === secret2.getX().toHex()
|
|
505
|
-
})
|
|
506
|
-
|
|
507
|
-
// Test 3: Invalid key rejection
|
|
508
|
-
test('Invalid key rejection', () => {
|
|
509
|
-
const privKey = PrivateKey.fromRandom()
|
|
510
|
-
const invalidPubKey = new PublicKey(new BigNumber(14), new BigNumber(16))
|
|
511
|
-
try {
|
|
512
|
-
privKey.deriveSharedSecret(invalidPubKey)
|
|
513
|
-
return false // Should have thrown
|
|
514
|
-
} catch (error) {
|
|
515
|
-
return error.message.includes('not valid for ECDH')
|
|
516
|
-
}
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
console.log(`\nTest Results: ${passed}/${total} passed`)
|
|
520
|
-
return passed === total
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
testECDHImplementation()
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
## Conclusion
|
|
527
|
-
|
|
528
|
-
In this tutorial, you've learned how to implement ECDH key exchange using the BSV TypeScript SDK. You now understand:
|
|
529
|
-
|
|
530
|
-
- The mathematical principles behind ECDH
|
|
531
|
-
- How to generate key pairs and derive shared secrets
|
|
532
|
-
- Security considerations and validation
|
|
533
|
-
- Practical applications including secure messaging
|
|
534
|
-
- Advanced patterns like authenticated key exchange
|
|
535
|
-
- Performance optimization and error handling
|
|
536
|
-
|
|
537
|
-
The BSV TypeScript SDK provides robust ECDH implementation with built-in security validations, making it safe and easy to implement secure key exchange protocols.
|
|
538
|
-
|
|
539
|
-
## Next Steps
|
|
540
|
-
|
|
541
|
-
- **[Script Construction](./script-construction.md)**: Learn to create custom Bitcoin scripts
|
|
542
|
-
- **[Advanced Transaction Construction](./advanced-transaction.md)**: Build complex transactions
|
|
543
|
-
- **[SPV and Merkle Proofs](./spv-merkle-proofs.md)**: Implement lightweight verification
|
|
544
|
-
|
|
545
|
-
## Further Reading
|
|
546
|
-
|
|
547
|
-
- [RFC 3526 - Diffie-Hellman Key Agreement](https://tools.ietf.org/html/rfc3526)
|
|
548
|
-
- [SEC 1: Elliptic Curve Cryptography](https://www.secg.org/sec1-v2.pdf)
|
|
549
|
-
- [BSV TypeScript SDK Documentation](../reference/primitives.md)
|