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

lightningnetwork / lnd / 15951470896

29 Jun 2025 04:23AM UTC coverage: 67.594% (-0.01%) from 67.606%
15951470896

Pull #9751

github

web-flow
Merge 599d9b051 into 6290edf14
Pull Request #9751: multi: update Go to 1.23.10 and update some packages

135088 of 199851 relevant lines covered (67.59%)

21909.44 hits per line

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

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

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

27✔
95
        // Check if we've already unlocked the store; return an error if so.
27✔
96
        if r.encKey != nil {
28✔
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 {
27✔
102
                return ErrPasswordRequired
1✔
103
        }
1✔
104

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

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

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

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

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

142
                r.encKey = encKey
15✔
143
                return nil
15✔
144
        }, func() {})
25✔
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 {
9✔
150
        // We need the store to already be unlocked. With this we can make sure
9✔
151
        // that there already is a key in the DB.
9✔
152
        if r.encKey == nil {
10✔
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 {
9✔
158
                return ErrPasswordRequired
1✔
159
        }
1✔
160

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

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

186
                // Create a new encryption key from the new password.
187
                encKeyNew, err := snacl.NewSecretKey(
6✔
188
                        &newPw, scryptN, scryptR, scryptP,
6✔
189
                )
6✔
190
                if err != nil {
6✔
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
6✔
198
                err = bucket.ForEach(func(k, v []byte) error {
16✔
199
                        // Skip the key if it is the encryption key ID since
10✔
200
                        // we do not want to re-encrypt this.
10✔
201
                        if bytes.Equal(k, encryptionKeyID) {
16✔
202
                                return nil
6✔
203
                        }
6✔
204

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

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

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

228
                if !foundDefaultRootKey {
6✔
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())
6✔
235
                if err != nil {
6✔
236
                        return err
×
237
                }
×
238

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

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

11✔
249
        if r.encKey == nil {
13✔
250
                return nil, ErrStoreLocked
2✔
251
        }
2✔
252
        var rootKey []byte
9✔
253
        err := kvdb.View(r.Backend, func(tx kvdb.RTx) error {
18✔
254
                bucket := tx.ReadBucket(rootKeyBucketName)
9✔
255
                if bucket == nil {
9✔
256
                        return ErrRootKeyBucketNotFound
×
257
                }
×
258
                dbKey := bucket.Get(id)
9✔
259
                if len(dbKey) == 0 {
12✔
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)
9✔
265
                if err != nil {
9✔
266
                        return err
×
267
                }
×
268

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

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

31✔
288
        if r.encKey == nil {
33✔
289
                return nil, nil, ErrStoreLocked
2✔
290
        }
2✔
291
        var rootKey []byte
29✔
292

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

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

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

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

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

336
        return rootKey, id, nil
26✔
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 {
27✔
412
        r.encKeyMtx.Lock()
27✔
413
        defer r.encKeyMtx.Unlock()
27✔
414

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

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

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

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

457
        var rootKeySlice [][]byte
5✔
458

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

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

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

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

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

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

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

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

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

5✔
512
                // Check the key can be found. If not, return nil.
5✔
513
                if bucket.Get(rootKeyID) == nil {
6✔
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 {
4✔
519
                        return err
×
520
                }
×
521
                rootKeyIDDeleted = rootKeyID
4✔
522

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

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