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

lightningnetwork / lnd / 13236757158

10 Feb 2025 08:39AM UTC coverage: 57.649% (-1.2%) from 58.815%
13236757158

Pull #9493

github

ziggie1984
lncli: for some cmds we don't replace the data of the response.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe it into sendtoRoute.
Pull Request #9493: For some lncli cmds we should not replace the content with other data

0 of 9 new or added lines in 2 files covered. (0.0%)

19535 existing lines in 252 files now uncovered.

103517 of 179563 relevant lines covered (57.65%)

24878.49 hits per line

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

79.7
/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) {
21✔
73
        // If the store's bucket doesn't exist, create it.
21✔
74
        err := kvdb.Update(db, func(tx kvdb.RwTx) error {
42✔
75
                _, err := tx.CreateTopLevelBucket(rootKeyBucketName)
21✔
76
                return err
21✔
77
        }, func() {})
42✔
78
        if err != nil {
21✔
79
                return nil, err
×
80
        }
×
81

82
        // Return the DB wrapped in a RootKeyStorage object.
83
        return &RootKeyStorage{
21✔
84
                Backend: db,
21✔
85
                encKey:  nil,
21✔
86
        }, nil
21✔
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 {
24✔
92
        r.encKeyMtx.Lock()
24✔
93
        defer r.encKeyMtx.Unlock()
24✔
94

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

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

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

120
                        err = encKey.DeriveKey(password)
9✔
121
                        if err != nil {
10✔
122
                                return err
1✔
123
                        }
1✔
124

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

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

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

142
                r.encKey = encKey
12✔
143
                return nil
12✔
144
        }, func() {})
22✔
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 {
6✔
150
        // We need the store to already be unlocked. With this we can make sure
6✔
151
        // that there already is a key in the DB.
6✔
152
        if r.encKey == nil {
7✔
153
                return ErrStoreLocked
1✔
154
        }
1✔
155

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

161
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
8✔
162
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
4✔
163
                if bucket == nil {
4✔
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)
4✔
170
                if len(encKeyDB) == 0 {
4✔
171
                        return ErrEncKeyNotFound
×
172
                }
×
173

174
                // Unmarshal parameters for old encryption key and derive the
175
                // old key with them.
176
                encKeyOld := &snacl.SecretKey{}
4✔
177
                err := encKeyOld.Unmarshal(encKeyDB)
4✔
178
                if err != nil {
4✔
179
                        return err
×
180
                }
×
181
                err = encKeyOld.DeriveKey(&oldPw)
4✔
182
                if err != nil {
5✔
183
                        return err
1✔
184
                }
1✔
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 {
10✔
199
                        // Skip the key if it is the encryption key ID since
7✔
200
                        // we do not want to re-encrypt this.
7✔
201
                        if bytes.Equal(k, encryptionKeyID) {
10✔
202
                                return nil
3✔
203
                        }
3✔
204

205
                        if bytes.Equal(k, DefaultRootKeyID) {
7✔
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)
4✔
213
                        if err != nil {
4✔
214
                                return err
×
215
                        }
×
216

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

222
                        return bucket.Put(k, encryptedKey)
4✔
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() {})
4✔
242
}
243

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

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

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

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

279
        return rootKey, nil
6✔
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) {
28✔
285
        r.encKeyMtx.RLock()
28✔
286
        defer r.encKeyMtx.RUnlock()
28✔
287

28✔
288
        if r.encKey == nil {
30✔
289
                return nil, nil, ErrStoreLocked
2✔
290
        }
2✔
291
        var rootKey []byte
26✔
292

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

300
        if bytes.Equal(id, encryptionKeyID) {
25✔
301
                return nil, nil, ErrKeyValueForbidden
1✔
302
        }
1✔
303

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

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

319
                        rootKey = make([]byte, len(decKey))
6✔
320
                        copy(rootKey[:], decKey[:])
6✔
321
                        return nil
6✔
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)
17✔
327
                rootKey = newKey
17✔
328
                return err
17✔
329
        }, func() {
23✔
330
                rootKey = nil
23✔
331
        })
23✔
332
        if err != nil {
23✔
333
                return nil, nil, err
×
334
        }
×
335

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

339
// GenerateNewRootKey generates a new macaroon root key, replacing the previous
340
// root key if it existed.
341
func (r *RootKeyStorage) GenerateNewRootKey() error {
4✔
342
        // We need the store to already be unlocked. With this we can make sure
4✔
343
        // that there already is a key in the DB that can be replaced.
4✔
344
        if r.encKey == nil {
5✔
345
                return ErrStoreLocked
1✔
346
        }
1✔
347
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
6✔
348
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
3✔
349
                if bucket == nil {
3✔
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.
356
                _, err := generateAndStoreNewRootKey(
3✔
357
                        bucket, DefaultRootKeyID, r.encKey,
3✔
358
                )
3✔
359
                if err != nil {
3✔
360
                        return err
×
361
                }
×
362

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

370
                        if bytes.Equal(k, DefaultRootKeyID) {
7✔
371
                                return nil
3✔
372
                        }
3✔
373

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

1✔
378
                        return err
1✔
379
                })
380
        }, func() {})
3✔
381
}
382

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

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

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

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

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

24✔
415
        if r.encKey != nil {
44✔
416
                r.encKey.Zero()
20✔
417
                r.encKey = nil
20✔
418
        }
20✔
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
24✔
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) {
21✔
433

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

439
        encryptedKey, err := key.Encrypt(rootKey)
21✔
440
        if err != nil {
21✔
441
                return nil, err
×
442
        }
×
443
        return rootKey, bucket.Put(id, encryptedKey)
21✔
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) {
2✔
449
        r.encKeyMtx.RLock()
2✔
450
        defer r.encKeyMtx.RUnlock()
2✔
451

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

457
        var rootKeySlice [][]byte
2✔
458

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

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

480
        return rootKeySlice, nil
2✔
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) {
5✔
487

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

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

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

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

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

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

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

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

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

531
        return rootKeyIDDeleted, nil
2✔
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