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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

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

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

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

105
        return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error {
41✔
106
                bucket := tx.ReadWriteBucket(rootKeyBucketName)
20✔
107
                if bucket == nil {
20✔
108
                        return ErrRootKeyBucketNotFound
×
109
                }
×
110
                dbKey := bucket.Get(encryptionKeyID)
20✔
111
                if len(dbKey) > 0 {
29✔
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(
11✔
131
                        password, scryptN, scryptR, scryptP,
11✔
132
                )
11✔
133
                if err != nil {
11✔
134
                        return err
×
135
                }
×
136

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

142
                r.encKey = encKey
11✔
143
                return nil
11✔
144
        }, func() {})
21✔
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) {
6✔
246
        r.encKeyMtx.RLock()
6✔
247
        defer r.encKeyMtx.RUnlock()
6✔
248

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

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

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

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

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

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

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

22✔
311
                // If there's a root key stored in the bucket, decrypt it and
22✔
312
                // return it.
22✔
313
                if len(dbKey) != 0 {
28✔
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)
16✔
327
                rootKey = newKey
16✔
328
                return err
16✔
329
        }, func() {
22✔
330
                rootKey = nil
22✔
331
        })
22✔
332
        if err != nil {
22✔
333
                return nil, nil, err
×
334
        }
×
335

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

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

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

439
        encryptedKey, err := key.Encrypt(rootKey)
20✔
440
        if err != nil {
20✔
441
                return nil, err
×
442
        }
×
443
        return rootKey, bucket.Put(id, encryptedKey)
20✔
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