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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 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