• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.69
/macaroons/store.go
1
package macaroons
2

3
import (
4
        "bytes"
5
        "context"
6
        "crypto/rand"
7
        "fmt"
8
        "io"
9
        "sync"
10

11
        "github.com/btcsuite/btcwallet/snacl"
12
        "github.com/btcsuite/btcwallet/walletdb"
13
        "github.com/lightningnetwork/lnd/kvdb"
14
)
15

16
const (
17
        // RootKeyLen is the length of a root key.
18
        RootKeyLen = 32
19
)
20

21
var (
22
        // rootKeyBucketName is the name of the root key store bucket.
23
        rootKeyBucketName = []byte("macrootkeys")
24

25
        // DefaultRootKeyID is the ID of the default root key. The first is
26
        // just 0, to emulate the memory storage that comes with bakery.
27
        DefaultRootKeyID = []byte("0")
28

29
        // encryptionKeyID is the name of the database key that stores the
30
        // encryption key, encrypted with a salted + hashed password. The
31
        // format is 32 bytes of salt, and the rest is encrypted key.
32
        encryptionKeyID = []byte("enckey")
33

34
        // ErrAlreadyUnlocked specifies that the store has already been
35
        // unlocked.
36
        ErrAlreadyUnlocked = fmt.Errorf("macaroon store already unlocked")
37

38
        // ErrStoreLocked specifies that the store needs to be unlocked with
39
        // a password.
40
        ErrStoreLocked = fmt.Errorf("macaroon store is locked")
41

42
        // ErrPasswordRequired specifies that a nil password has been passed.
43
        ErrPasswordRequired = fmt.Errorf("a non-nil password is required")
44

45
        // ErrKeyValueForbidden is used when the root key ID uses encryptedKeyID as
46
        // its value.
47
        ErrKeyValueForbidden = fmt.Errorf("root key ID value is not allowed")
48

49
        // ErrRootKeyBucketNotFound specifies that there is no macaroon root key
50
        // bucket yet which can/should only happen if the store has been
51
        // corrupted or was initialized incorrectly.
52
        ErrRootKeyBucketNotFound = fmt.Errorf("root key bucket not found")
53

54
        // ErrEncKeyNotFound specifies that there was no encryption key found
55
        // even if one was expected to be generated.
56
        ErrEncKeyNotFound = fmt.Errorf("macaroon encryption key not found")
57

58
        // ErrDefaultRootKeyNotFound is returned when the default root key is
59
        // not found in the DB when it is expected to be.
60
        ErrDefaultRootKeyNotFound = fmt.Errorf("default root key not found")
61
)
62

63
// RootKeyStorage implements the bakery.RootKeyStorage interface.
64
type RootKeyStorage struct {
65
        kvdb.Backend
66

67
        encKeyMtx sync.RWMutex
68
        encKey    *snacl.SecretKey
69
}
70

71
// NewRootKeyStorage creates a RootKeyStorage instance.
72
func NewRootKeyStorage(db kvdb.Backend) (*RootKeyStorage, error) {
3✔
73
        // If the store's bucket doesn't exist, create it.
3✔
74
        err := kvdb.Update(db, func(tx kvdb.RwTx) error {
6✔
75
                _, err := tx.CreateTopLevelBucket(rootKeyBucketName)
3✔
76
                return err
3✔
77
        }, func() {})
6✔
78
        if err != nil {
3✔
79
                return nil, err
×
80
        }
×
81

82
        // Return the DB wrapped in a RootKeyStorage object.
83
        return &RootKeyStorage{
3✔
84
                Backend: db,
3✔
85
                encKey:  nil,
3✔
86
        }, nil
3✔
87
}
88

89
// CreateUnlock sets an encryption key if one is not already set, otherwise it
90
// checks if the password is correct for the stored encryption key.
91
func (r *RootKeyStorage) CreateUnlock(password *[]byte) error {
3✔
92
        r.encKeyMtx.Lock()
3✔
93
        defer r.encKeyMtx.Unlock()
3✔
94

3✔
95
        // Check if we've already unlocked the store; return an error if so.
3✔
96
        if r.encKey != nil {
3✔
UNCOV
97
                return ErrAlreadyUnlocked
×
UNCOV
98
        }
×
99

100
        // Check if a nil password has been passed; return an error if so.
101
        if password == nil {
3✔
UNCOV
102
                return ErrPasswordRequired
×
UNCOV
103
        }
×
104

105
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
6✔
106
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
3✔
107
                if bucket == nil {
3✔
108
                        return ErrRootKeyBucketNotFound
×
109
                }
×
110
                dbKey := bucket.Get(encryptionKeyID)
3✔
111
                if len(dbKey) > 0 {
6✔
112
                        // We've already stored a key, so try to unlock with
3✔
113
                        // the password.
3✔
114
                        encKey := &snacl.SecretKey{}
3✔
115
                        err := encKey.Unmarshal(dbKey)
3✔
116
                        if err != nil {
3✔
117
                                return err
×
118
                        }
×
119

120
                        err = encKey.DeriveKey(password)
3✔
121
                        if err != nil {
3✔
UNCOV
122
                                return err
×
UNCOV
123
                        }
×
124

125
                        r.encKey = encKey
3✔
126
                        return nil
3✔
127
                }
128

129
                // We haven't yet stored a key, so create a new one.
130
                encKey, err := snacl.NewSecretKey(
3✔
131
                        password, scryptN, scryptR, scryptP,
3✔
132
                )
3✔
133
                if err != nil {
3✔
134
                        return err
×
135
                }
×
136

137
                err = bucket.Put(encryptionKeyID, encKey.Marshal())
3✔
138
                if err != nil {
3✔
139
                        return err
×
140
                }
×
141

142
                r.encKey = encKey
3✔
143
                return nil
3✔
144
        }, func() {})
3✔
145
}
146

147
// ChangePassword decrypts all the macaroon root keys with the old password and
148
// then encrypts them again with the new password.
149
func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error {
3✔
150
        // We need the store to already be unlocked. With this we can make sure
3✔
151
        // that there already is a key in the DB.
3✔
152
        if r.encKey == nil {
3✔
UNCOV
153
                return ErrStoreLocked
×
UNCOV
154
        }
×
155

156
        // Check if a nil password has been passed; return an error if so.
157
        if oldPw == nil || newPw == nil {
3✔
UNCOV
158
                return ErrPasswordRequired
×
UNCOV
159
        }
×
160

161
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
6✔
162
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
3✔
163
                if bucket == nil {
3✔
164
                        return ErrRootKeyBucketNotFound
×
165
                }
×
166

167
                // The encryption key must be present, otherwise we are in the
168
                // wrong state to change the password.
169
                encKeyDB := bucket.Get(encryptionKeyID)
3✔
170
                if len(encKeyDB) == 0 {
3✔
171
                        return ErrEncKeyNotFound
×
172
                }
×
173

174
                // Unmarshal parameters for old encryption key and derive the
175
                // old key with them.
176
                encKeyOld := &snacl.SecretKey{}
3✔
177
                err := encKeyOld.Unmarshal(encKeyDB)
3✔
178
                if err != nil {
3✔
179
                        return err
×
180
                }
×
181
                err = encKeyOld.DeriveKey(&oldPw)
3✔
182
                if err != nil {
3✔
UNCOV
183
                        return err
×
UNCOV
184
                }
×
185

186
                // Create a new encryption key from the new password.
187
                encKeyNew, err := snacl.NewSecretKey(
3✔
188
                        &newPw, scryptN, scryptR, scryptP,
3✔
189
                )
3✔
190
                if err != nil {
3✔
191
                        return err
×
192
                }
×
193

194
                // foundDefaultRootKey is used to keep track of if we have
195
                // found and re-encrypted the default root key so that we can
196
                // return an error if it is not found.
197
                var foundDefaultRootKey bool
3✔
198
                err = bucket.ForEach(func(k, v []byte) error {
6✔
199
                        // Skip the key if it is the encryption key ID since
3✔
200
                        // we do not want to re-encrypt this.
3✔
201
                        if bytes.Equal(k, encryptionKeyID) {
6✔
202
                                return nil
3✔
203
                        }
3✔
204

205
                        if bytes.Equal(k, DefaultRootKeyID) {
6✔
206
                                foundDefaultRootKey = true
3✔
207
                        }
3✔
208

209
                        // Now try to decrypt the root key with the old
210
                        // encryption key, encrypt it with the new one and then
211
                        // store it in the DB.
212
                        decryptedKey, err := encKeyOld.Decrypt(v)
3✔
213
                        if err != nil {
3✔
214
                                return err
×
215
                        }
×
216

217
                        encryptedKey, err := encKeyNew.Encrypt(decryptedKey)
3✔
218
                        if err != nil {
3✔
219
                                return err
×
220
                        }
×
221

222
                        return bucket.Put(k, encryptedKey)
3✔
223
                })
224
                if err != nil {
3✔
225
                        return err
×
226
                }
×
227

228
                if !foundDefaultRootKey {
3✔
229
                        return ErrDefaultRootKeyNotFound
×
230
                }
×
231

232
                // Finally, store the new encryption key parameters in the DB
233
                // as well.
234
                err = bucket.Put(encryptionKeyID, encKeyNew.Marshal())
3✔
235
                if err != nil {
3✔
236
                        return err
×
237
                }
×
238

239
                r.encKey = encKeyNew
3✔
240
                return nil
3✔
241
        }, func() {})
3✔
242
}
243

244
// Get implements the Get method for the bakery.RootKeyStorage interface.
245
func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
3✔
246
        r.encKeyMtx.RLock()
3✔
247
        defer r.encKeyMtx.RUnlock()
3✔
248

3✔
249
        if r.encKey == nil {
3✔
UNCOV
250
                return nil, ErrStoreLocked
×
UNCOV
251
        }
×
252
        var rootKey []byte
3✔
253
        err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
6✔
254
                bucket := tx.ReadBucket(rootKeyBucketName)
3✔
255
                if bucket == nil {
3✔
256
                        return ErrRootKeyBucketNotFound
×
257
                }
×
258
                dbKey := bucket.Get(id)
3✔
259
                if len(dbKey) == 0 {
6✔
260
                        return fmt.Errorf("root key with id %s doesn't exist",
3✔
261
                                string(id))
3✔
262
                }
3✔
263

264
                decKey, err := r.encKey.Decrypt(dbKey)
3✔
265
                if err != nil {
3✔
266
                        return err
×
267
                }
×
268

269
                rootKey = make([]byte, len(decKey))
3✔
270
                copy(rootKey[:], decKey)
3✔
271
                return nil
3✔
272
        }, func() {
3✔
273
                rootKey = nil
3✔
274
        })
3✔
275
        if err != nil {
6✔
276
                return nil, err
3✔
277
        }
3✔
278

279
        return rootKey, nil
3✔
280
}
281

282
// RootKey implements the RootKey method for the bakery.RootKeyStorage
283
// interface.
284
func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) {
3✔
285
        r.encKeyMtx.RLock()
3✔
286
        defer r.encKeyMtx.RUnlock()
3✔
287

3✔
288
        if r.encKey == nil {
3✔
UNCOV
289
                return nil, nil, ErrStoreLocked
×
UNCOV
290
        }
×
291
        var rootKey []byte
3✔
292

3✔
293
        // Read the root key ID from the context. If no key is specified in the
3✔
294
        // context, an error will be returned.
3✔
295
        id, err := RootKeyIDFromContext(ctx)
3✔
296
        if err != nil {
3✔
UNCOV
297
                return nil, nil, err
×
UNCOV
298
        }
×
299

300
        if bytes.Equal(id, encryptionKeyID) {
3✔
UNCOV
301
                return nil, nil, ErrKeyValueForbidden
×
UNCOV
302
        }
×
303

304
        err = kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
6✔
305
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
3✔
306
                if bucket == nil {
3✔
307
                        return ErrRootKeyBucketNotFound
×
308
                }
×
309
                dbKey := bucket.Get(id)
3✔
310

3✔
311
                // If there's a root key stored in the bucket, decrypt it and
3✔
312
                // return it.
3✔
313
                if len(dbKey) != 0 {
6✔
314
                        decKey, err := r.encKey.Decrypt(dbKey)
3✔
315
                        if err != nil {
3✔
316
                                return err
×
317
                        }
×
318

319
                        rootKey = make([]byte, len(decKey))
3✔
320
                        copy(rootKey[:], decKey[:])
3✔
321
                        return nil
3✔
322
                }
323

324
                // Otherwise, create a new root key, encrypt it,
325
                // and store it in the bucket.
326
                newKey, err := generateAndStoreNewRootKey(bucket, id, r.encKey)
3✔
327
                rootKey = newKey
3✔
328
                return err
3✔
329
        }, func() {
3✔
330
                rootKey = nil
3✔
331
        })
3✔
332
        if err != nil {
3✔
333
                return nil, nil, err
×
334
        }
×
335

336
        return rootKey, id, nil
3✔
337
}
338

339
// GenerateNewRootKey generates a new macaroon root key, replacing the previous
340
// root key if it existed.
UNCOV
341
func (r *RootKeyStorage) GenerateNewRootKey() error {
×
UNCOV
342
        // We need the store to already be unlocked. With this we can make sure
×
UNCOV
343
        // that there already is a key in the DB that can be replaced.
×
UNCOV
344
        if r.encKey == nil {
×
UNCOV
345
                return ErrStoreLocked
×
UNCOV
346
        }
×
UNCOV
347
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
×
UNCOV
348
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
×
UNCOV
349
                if bucket == nil {
×
350
                        return ErrRootKeyBucketNotFound
×
351
                }
×
352

353
                // The default root key should be created even if it does not
354
                // yet exist, so we do this separately from the rest of the
355
                // root keys.
UNCOV
356
                _, err := generateAndStoreNewRootKey(
×
UNCOV
357
                        bucket, DefaultRootKeyID, r.encKey,
×
UNCOV
358
                )
×
UNCOV
359
                if err != nil {
×
360
                        return err
×
361
                }
×
362

363
                // Now iterate over all the other root keys that may exist
364
                // and re-generate each of them.
UNCOV
365
                return bucket.ForEach(func(k, v []byte) error {
×
UNCOV
366
                        if bytes.Equal(k, encryptionKeyID) {
×
UNCOV
367
                                return nil
×
UNCOV
368
                        }
×
369

UNCOV
370
                        if bytes.Equal(k, DefaultRootKeyID) {
×
UNCOV
371
                                return nil
×
UNCOV
372
                        }
×
373

UNCOV
374
                        _, err := generateAndStoreNewRootKey(
×
UNCOV
375
                                bucket, k, r.encKey,
×
UNCOV
376
                        )
×
UNCOV
377

×
UNCOV
378
                        return err
×
379
                })
UNCOV
380
        }, func() {})
×
381
}
382

383
// SetRootKey sets the default macaroon root key, replacing the previous root
384
// key if it existed.
UNCOV
385
func (r *RootKeyStorage) SetRootKey(rootKey []byte) error {
×
UNCOV
386
        if r.encKey == nil {
×
UNCOV
387
                return ErrStoreLocked
×
UNCOV
388
        }
×
UNCOV
389
        if len(rootKey) != RootKeyLen {
×
390
                return fmt.Errorf("root key must be %v bytes",
×
391
                        RootKeyLen)
×
392
        }
×
393

UNCOV
394
        encryptedKey, err := r.encKey.Encrypt(rootKey)
×
UNCOV
395
        if err != nil {
×
396
                return err
×
397
        }
×
398

UNCOV
399
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
×
UNCOV
400
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
×
UNCOV
401
                if bucket == nil {
×
402
                        return ErrRootKeyBucketNotFound
×
403
                }
×
404

UNCOV
405
                return bucket.Put(DefaultRootKeyID, encryptedKey)
×
UNCOV
406
        }, func() {})
×
407
}
408

409
// Close closes the underlying database and zeroes the encryption key stored
410
// in memory.
411
func (r *RootKeyStorage) Close() error {
3✔
412
        r.encKeyMtx.Lock()
3✔
413
        defer r.encKeyMtx.Unlock()
3✔
414

3✔
415
        if r.encKey != nil {
6✔
416
                r.encKey.Zero()
3✔
417
                r.encKey = nil
3✔
418
        }
3✔
419

420
        // Since we're not responsible for _creating_ the connection to our DB
421
        // backend, we also shouldn't close it. This should be handled
422
        // externally as to not interfere with remote DB connections in case we
423
        // need to open/close the store twice as happens in the password change
424
        // case.
425
        return nil
3✔
426
}
427

428
// generateAndStoreNewRootKey creates a new random RootKeyLen-byte root key,
429
// encrypts it with the given encryption key and stores it in the bucket.
430
// Any previously set key will be overwritten.
431
func generateAndStoreNewRootKey(bucket walletdb.ReadWriteBucket, id []byte,
432
        key *snacl.SecretKey) ([]byte, error) {
3✔
433

3✔
434
        rootKey := make([]byte, RootKeyLen)
3✔
435
        if _, err := io.ReadFull(rand.Reader, rootKey); err != nil {
3✔
436
                return nil, err
×
437
        }
×
438

439
        encryptedKey, err := key.Encrypt(rootKey)
3✔
440
        if err != nil {
3✔
441
                return nil, err
×
442
        }
×
443
        return rootKey, bucket.Put(id, encryptedKey)
3✔
444
}
445

446
// ListMacaroonIDs returns all the root key ID values except the value of
447
// encryptedKeyID.
448
func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) {
3✔
449
        r.encKeyMtx.RLock()
3✔
450
        defer r.encKeyMtx.RUnlock()
3✔
451

3✔
452
        // Check it's unlocked.
3✔
453
        if r.encKey == nil {
3✔
454
                return nil, ErrStoreLocked
×
455
        }
×
456

457
        var rootKeySlice [][]byte
3✔
458

3✔
459
        // Read all the items in the bucket and append the keys, which are the
3✔
460
        // root key IDs we want.
3✔
461
        err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
6✔
462
                // appendRootKey is a function closure that appends root key ID
3✔
463
                // to rootKeySlice.
3✔
464
                appendRootKey := func(k, _ []byte) error {
6✔
465
                        // Only append when the key value is not encryptedKeyID.
3✔
466
                        if !bytes.Equal(k, encryptionKeyID) {
6✔
467
                                rootKeySlice = append(rootKeySlice, k)
3✔
468
                        }
3✔
469
                        return nil
3✔
470
                }
471

472
                return tx.ReadBucket(rootKeyBucketName).ForEach(appendRootKey)
3✔
473
        }, func() {
3✔
474
                rootKeySlice = nil
3✔
475
        })
3✔
476
        if err != nil {
3✔
477
                return nil, err
×
478
        }
×
479

480
        return rootKeySlice, nil
3✔
481
}
482

483
// DeleteMacaroonID removes one specific root key ID. If the root key ID is
484
// found and deleted, it will be returned.
485
func (r *RootKeyStorage) DeleteMacaroonID(
486
        _ context.Context, rootKeyID []byte) ([]byte, error) {
3✔
487

3✔
488
        r.encKeyMtx.RLock()
3✔
489
        defer r.encKeyMtx.RUnlock()
3✔
490

3✔
491
        // Check it's unlocked.
3✔
492
        if r.encKey == nil {
3✔
493
                return nil, ErrStoreLocked
×
494
        }
×
495

496
        // Check the rootKeyID is not empty.
497
        if len(rootKeyID) == 0 {
3✔
UNCOV
498
                return nil, ErrMissingRootKeyID
×
UNCOV
499
        }
×
500

501
        // Deleting encryptedKeyID or DefaultRootKeyID is not allowed.
502
        if bytes.Equal(rootKeyID, encryptionKeyID) ||
3✔
503
                bytes.Equal(rootKeyID, DefaultRootKeyID) {
6✔
504

3✔
505
                return nil, ErrDeletionForbidden
3✔
506
        }
3✔
507

508
        var rootKeyIDDeleted []byte
3✔
509
        err := kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
6✔
510
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
3✔
511

3✔
512
                // Check the key can be found. If not, return nil.
3✔
513
                if bucket.Get(rootKeyID) == nil {
3✔
UNCOV
514
                        return nil
×
UNCOV
515
                }
×
516

517
                // Once the key is found, we do the deletion.
518
                if err := bucket.Delete(rootKeyID); err != nil {
3✔
519
                        return err
×
520
                }
×
521
                rootKeyIDDeleted = rootKeyID
3✔
522

3✔
523
                return nil
3✔
524
        }, func() {
3✔
525
                rootKeyIDDeleted = nil
3✔
526
        })
3✔
527
        if err != nil {
3✔
528
                return nil, err
×
529
        }
×
530

531
        return rootKeyIDDeleted, nil
3✔
532
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc