github.com/decred/dcrlnd@v0.7.6/macaroons/store.go (about) 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/walletdb" 12 "github.com/decred/dcrlnd/kvdb" 13 14 "github.com/decred/dcrlnd/internal/snacl" 15 ) 16 17 const ( 18 // RootKeyLen is the length of a root key. 19 RootKeyLen = 32 20 ) 21 22 var ( 23 // rootKeyBucketName is the name of the root key store bucket. 24 rootKeyBucketName = []byte("macrootkeys") 25 26 // DefaultRootKeyID is the ID of the default root key. The first is 27 // just 0, to emulate the memory storage that comes with bakery. 28 DefaultRootKeyID = []byte("0") 29 30 // encryptionKeyID is the name of the database key that stores the 31 // encryption key, encrypted with a salted + hashed password. The 32 // format is 32 bytes of salt, and the rest is encrypted key. 33 encryptionKeyID = []byte("enckey") 34 35 // ErrAlreadyUnlocked specifies that the store has already been 36 // unlocked. 37 ErrAlreadyUnlocked = fmt.Errorf("macaroon store already unlocked") 38 39 // ErrStoreLocked specifies that the store needs to be unlocked with 40 // a password. 41 ErrStoreLocked = fmt.Errorf("macaroon store is locked") 42 43 // ErrPasswordRequired specifies that a nil password has been passed. 44 ErrPasswordRequired = fmt.Errorf("a non-nil password is required") 45 46 // ErrKeyValueForbidden is used when the root key ID uses encryptedKeyID as 47 // its value. 48 ErrKeyValueForbidden = fmt.Errorf("root key ID value is not allowed") 49 50 // ErrRootKeyBucketNotFound specifies that there is no macaroon root key 51 // bucket yet which can/should only happen if the store has been 52 // corrupted or was initialized incorrectly. 53 ErrRootKeyBucketNotFound = fmt.Errorf("root key bucket not found") 54 55 // ErrEncKeyNotFound specifies that there was no encryption key found 56 // even if one was expected to be generated. 57 ErrEncKeyNotFound = fmt.Errorf("macaroon encryption key not found") 58 ) 59 60 // RootKeyStorage implements the bakery.RootKeyStorage interface. 61 type RootKeyStorage struct { 62 kvdb.Backend 63 64 encKeyMtx sync.RWMutex 65 encKey *snacl.SecretKey 66 } 67 68 // NewRootKeyStorage creates a RootKeyStorage instance. 69 func NewRootKeyStorage(db kvdb.Backend) (*RootKeyStorage, error) { 70 // If the store's bucket doesn't exist, create it. 71 err := kvdb.Update(db, func(tx kvdb.RwTx) error { 72 _, err := tx.CreateTopLevelBucket(rootKeyBucketName) 73 return err 74 }, func() {}) 75 if err != nil { 76 return nil, err 77 } 78 79 // Return the DB wrapped in a RootKeyStorage object. 80 return &RootKeyStorage{ 81 Backend: db, 82 encKey: nil, 83 }, nil 84 } 85 86 // CreateUnlock sets an encryption key if one is not already set, otherwise it 87 // checks if the password is correct for the stored encryption key. 88 func (r *RootKeyStorage) CreateUnlock(password *[]byte) error { 89 r.encKeyMtx.Lock() 90 defer r.encKeyMtx.Unlock() 91 92 // Check if we've already unlocked the store; return an error if so. 93 if r.encKey != nil { 94 return ErrAlreadyUnlocked 95 } 96 97 // Check if a nil password has been passed; return an error if so. 98 if password == nil { 99 return ErrPasswordRequired 100 } 101 102 return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error { 103 bucket := tx.ReadWriteBucket(rootKeyBucketName) 104 if bucket == nil { 105 return ErrRootKeyBucketNotFound 106 } 107 dbKey := bucket.Get(encryptionKeyID) 108 if len(dbKey) > 0 { 109 // We've already stored a key, so try to unlock with 110 // the password. 111 encKey := &snacl.SecretKey{} 112 err := encKey.Unmarshal(dbKey) 113 if err != nil { 114 return err 115 } 116 117 err = encKey.DeriveKey(password) 118 if err != nil { 119 return err 120 } 121 122 r.encKey = encKey 123 return nil 124 } 125 126 // We haven't yet stored a key, so create a new one. 127 encKey, err := snacl.NewSecretKey( 128 password, scryptN, scryptR, scryptP, 129 ) 130 if err != nil { 131 return err 132 } 133 134 err = bucket.Put(encryptionKeyID, encKey.Marshal()) 135 if err != nil { 136 return err 137 } 138 139 r.encKey = encKey 140 return nil 141 }, func() {}) 142 } 143 144 // ChangePassword decrypts the macaroon root key with the old password and then 145 // encrypts it again with the new password. 146 func (r *RootKeyStorage) ChangePassword(oldPw, newPw []byte) error { 147 // We need the store to already be unlocked. With this we can make sure 148 // that there already is a key in the DB. 149 if r.encKey == nil { 150 return ErrStoreLocked 151 } 152 153 // Check if a nil password has been passed; return an error if so. 154 if oldPw == nil || newPw == nil { 155 return ErrPasswordRequired 156 } 157 158 return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error { 159 bucket := tx.ReadWriteBucket(rootKeyBucketName) 160 if bucket == nil { 161 return ErrRootKeyBucketNotFound 162 } 163 encKeyDb := bucket.Get(encryptionKeyID) 164 rootKeyDb := bucket.Get(DefaultRootKeyID) 165 166 // Both the encryption key and the root key must be present 167 // otherwise we are in the wrong state to change the password. 168 if len(encKeyDb) == 0 || len(rootKeyDb) == 0 { 169 return ErrEncKeyNotFound 170 } 171 172 // Unmarshal parameters for old encryption key and derive the 173 // old key with them. 174 encKeyOld := &snacl.SecretKey{} 175 err := encKeyOld.Unmarshal(encKeyDb) 176 if err != nil { 177 return err 178 } 179 err = encKeyOld.DeriveKey(&oldPw) 180 if err != nil { 181 return err 182 } 183 184 // Create a new encryption key from the new password. 185 encKeyNew, err := snacl.NewSecretKey( 186 &newPw, scryptN, scryptR, scryptP, 187 ) 188 if err != nil { 189 return err 190 } 191 192 // Now try to decrypt the root key with the old encryption key, 193 // encrypt it with the new one and then store it in the DB. 194 decryptedKey, err := encKeyOld.Decrypt(rootKeyDb) 195 if err != nil { 196 return err 197 } 198 rootKey := make([]byte, len(decryptedKey)) 199 copy(rootKey, decryptedKey) 200 encryptedKey, err := encKeyNew.Encrypt(rootKey) 201 if err != nil { 202 return err 203 } 204 err = bucket.Put(DefaultRootKeyID, encryptedKey) 205 if err != nil { 206 return err 207 } 208 209 // Finally, store the new encryption key parameters in the DB 210 // as well. 211 err = bucket.Put(encryptionKeyID, encKeyNew.Marshal()) 212 if err != nil { 213 return err 214 } 215 216 r.encKey = encKeyNew 217 return nil 218 }, func() {}) 219 } 220 221 // Get implements the Get method for the bakery.RootKeyStorage interface. 222 func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) { 223 r.encKeyMtx.RLock() 224 defer r.encKeyMtx.RUnlock() 225 226 if r.encKey == nil { 227 return nil, ErrStoreLocked 228 } 229 var rootKey []byte 230 err := kvdb.View(r.Backend, func(tx kvdb.RTx) error { 231 bucket := tx.ReadBucket(rootKeyBucketName) 232 if bucket == nil { 233 return ErrRootKeyBucketNotFound 234 } 235 dbKey := bucket.Get(id) 236 if len(dbKey) == 0 { 237 return fmt.Errorf("root key with id %s doesn't exist", 238 string(id)) 239 } 240 241 decKey, err := r.encKey.Decrypt(dbKey) 242 if err != nil { 243 return err 244 } 245 246 rootKey = make([]byte, len(decKey)) 247 copy(rootKey, decKey) 248 return nil 249 }, func() { 250 rootKey = nil 251 }) 252 if err != nil { 253 return nil, err 254 } 255 256 return rootKey, nil 257 } 258 259 // RootKey implements the RootKey method for the bakery.RootKeyStorage 260 // interface. 261 func (r *RootKeyStorage) RootKey(ctx context.Context) ([]byte, []byte, error) { 262 r.encKeyMtx.RLock() 263 defer r.encKeyMtx.RUnlock() 264 265 if r.encKey == nil { 266 return nil, nil, ErrStoreLocked 267 } 268 var rootKey []byte 269 270 // Read the root key ID from the context. If no key is specified in the 271 // context, an error will be returned. 272 id, err := RootKeyIDFromContext(ctx) 273 if err != nil { 274 return nil, nil, err 275 } 276 277 if bytes.Equal(id, encryptionKeyID) { 278 return nil, nil, ErrKeyValueForbidden 279 } 280 281 err = kvdb.Update(r.Backend, func(tx kvdb.RwTx) error { 282 bucket := tx.ReadWriteBucket(rootKeyBucketName) 283 if bucket == nil { 284 return ErrRootKeyBucketNotFound 285 } 286 dbKey := bucket.Get(id) 287 288 // If there's a root key stored in the bucket, decrypt it and 289 // return it. 290 if len(dbKey) != 0 { 291 decKey, err := r.encKey.Decrypt(dbKey) 292 if err != nil { 293 return err 294 } 295 296 rootKey = make([]byte, len(decKey)) 297 copy(rootKey, decKey) 298 return nil 299 } 300 301 // Otherwise, create a new root key, encrypt it, 302 // and store it in the bucket. 303 newKey, err := generateAndStoreNewRootKey(bucket, id, r.encKey) 304 rootKey = newKey 305 return err 306 }, func() { 307 rootKey = nil 308 }) 309 if err != nil { 310 return nil, nil, err 311 } 312 313 return rootKey, id, nil 314 } 315 316 // GenerateNewRootKey generates a new macaroon root key, replacing the previous 317 // root key if it existed. 318 func (r *RootKeyStorage) GenerateNewRootKey() error { 319 // We need the store to already be unlocked. With this we can make sure 320 // that there already is a key in the DB that can be replaced. 321 if r.encKey == nil { 322 return ErrStoreLocked 323 } 324 return kvdb.Update(r.Backend, func(tx kvdb.RwTx) error { 325 bucket := tx.ReadWriteBucket(rootKeyBucketName) 326 if bucket == nil { 327 return ErrRootKeyBucketNotFound 328 } 329 _, err := generateAndStoreNewRootKey( 330 bucket, DefaultRootKeyID, r.encKey, 331 ) 332 return err 333 }, func() {}) 334 } 335 336 // Close closes the underlying database and zeroes the encryption key stored 337 // in memory. 338 func (r *RootKeyStorage) Close() error { 339 r.encKeyMtx.Lock() 340 defer r.encKeyMtx.Unlock() 341 342 if r.encKey != nil { 343 r.encKey.Zero() 344 r.encKey = nil 345 } 346 347 // Since we're not responsible for _creating_ the connection to our DB 348 // backend, we also shouldn't close it. This should be handled 349 // externally as to not interfere with remote DB connections in case we 350 // need to open/close the store twice as happens in the password change 351 // case. 352 return nil 353 } 354 355 // generateAndStoreNewRootKey creates a new random RootKeyLen-byte root key, 356 // encrypts it with the given encryption key and stores it in the bucket. 357 // Any previously set key will be overwritten. 358 func generateAndStoreNewRootKey(bucket walletdb.ReadWriteBucket, id []byte, 359 key *snacl.SecretKey) ([]byte, error) { 360 361 rootKey := make([]byte, RootKeyLen) 362 if _, err := io.ReadFull(rand.Reader, rootKey); err != nil { 363 return nil, err 364 } 365 366 encryptedKey, err := key.Encrypt(rootKey) 367 if err != nil { 368 return nil, err 369 } 370 return rootKey, bucket.Put(id, encryptedKey) 371 } 372 373 // ListMacaroonIDs returns all the root key ID values except the value of 374 // encryptedKeyID. 375 func (r *RootKeyStorage) ListMacaroonIDs(_ context.Context) ([][]byte, error) { 376 r.encKeyMtx.RLock() 377 defer r.encKeyMtx.RUnlock() 378 379 // Check it's unlocked. 380 if r.encKey == nil { 381 return nil, ErrStoreLocked 382 } 383 384 var rootKeySlice [][]byte 385 386 // Read all the items in the bucket and append the keys, which are the 387 // root key IDs we want. 388 err := kvdb.View(r.Backend, func(tx kvdb.RTx) error { 389 390 // appendRootKey is a function closure that appends root key ID 391 // to rootKeySlice. 392 appendRootKey := func(k, _ []byte) error { 393 // Only append when the key value is not encryptedKeyID. 394 if !bytes.Equal(k, encryptionKeyID) { 395 rootKeySlice = append(rootKeySlice, k) 396 } 397 return nil 398 } 399 400 return tx.ReadBucket(rootKeyBucketName).ForEach(appendRootKey) 401 }, func() { 402 rootKeySlice = nil 403 }) 404 if err != nil { 405 return nil, err 406 } 407 408 return rootKeySlice, nil 409 } 410 411 // DeleteMacaroonID removes one specific root key ID. If the root key ID is 412 // found and deleted, it will be returned. 413 func (r *RootKeyStorage) DeleteMacaroonID( 414 _ context.Context, rootKeyID []byte) ([]byte, error) { 415 416 r.encKeyMtx.RLock() 417 defer r.encKeyMtx.RUnlock() 418 419 // Check it's unlocked. 420 if r.encKey == nil { 421 return nil, ErrStoreLocked 422 } 423 424 // Check the rootKeyID is not empty. 425 if len(rootKeyID) == 0 { 426 return nil, ErrMissingRootKeyID 427 } 428 429 // Deleting encryptedKeyID or DefaultRootKeyID is not allowed. 430 if bytes.Equal(rootKeyID, encryptionKeyID) || 431 bytes.Equal(rootKeyID, DefaultRootKeyID) { 432 433 return nil, ErrDeletionForbidden 434 } 435 436 var rootKeyIDDeleted []byte 437 err := kvdb.Update(r.Backend, func(tx kvdb.RwTx) error { 438 bucket := tx.ReadWriteBucket(rootKeyBucketName) 439 440 // Check the key can be found. If not, return nil. 441 if bucket.Get(rootKeyID) == nil { 442 return nil 443 } 444 445 // Once the key is found, we do the deletion. 446 if err := bucket.Delete(rootKeyID); err != nil { 447 return err 448 } 449 rootKeyIDDeleted = rootKeyID 450 451 return nil 452 }, func() { 453 rootKeyIDDeleted = nil 454 }) 455 if err != nil { 456 return nil, err 457 } 458 459 return rootKeyIDDeleted, nil 460 }