github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/accounts/account_manager.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package accounts implements encrypted storage of secp256k1 private keys. 18 // 19 // Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification. 20 // See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information. 21 package accounts 22 23 import ( 24 "crypto/ecdsa" 25 crand "crypto/rand" 26 "encoding/json" 27 "errors" 28 "fmt" 29 "os" 30 "path/filepath" 31 "runtime" 32 "sync" 33 "time" 34 35 "github.com/ethereum/go-ethereum/common" 36 "github.com/ethereum/go-ethereum/crypto" 37 ) 38 39 var ( 40 ErrLocked = errors.New("account is locked") 41 ErrNoMatch = errors.New("no key for given address or file") 42 ErrDecrypt = errors.New("could not decrypt key with given passphrase") 43 ) 44 45 // Account represents a stored key. 46 // When used as an argument, it selects a unique key file to act on. 47 type Account struct { 48 Address common.Address // Ethereum account address derived from the key 49 50 // File contains the key file name. 51 // When Acccount is used as an argument to select a key, File can be left blank to 52 // select just by address or set to the basename or absolute path of a file in the key 53 // directory. Accounts returned by Manager will always contain an absolute path. 54 File string 55 } 56 57 func (acc *Account) MarshalJSON() ([]byte, error) { 58 return []byte(`"` + acc.Address.Hex() + `"`), nil 59 } 60 61 func (acc *Account) UnmarshalJSON(raw []byte) error { 62 return json.Unmarshal(raw, &acc.Address) 63 } 64 65 // Manager manages a key storage directory on disk. 66 type Manager struct { 67 cache *addrCache 68 keyStore keyStore 69 mu sync.RWMutex 70 unlocked map[common.Address]*unlocked 71 } 72 73 type unlocked struct { 74 *Key 75 abort chan struct{} 76 } 77 78 // NewManager creates a manager for the given directory. 79 func NewManager(keydir string, scryptN, scryptP int) *Manager { 80 keydir, _ = filepath.Abs(keydir) 81 am := &Manager{keyStore: &keyStorePassphrase{keydir, scryptN, scryptP}} 82 am.init(keydir) 83 return am 84 } 85 86 // NewPlaintextManager creates a manager for the given directory. 87 // Deprecated: Use NewManager. 88 func NewPlaintextManager(keydir string) *Manager { 89 keydir, _ = filepath.Abs(keydir) 90 am := &Manager{keyStore: &keyStorePlain{keydir}} 91 am.init(keydir) 92 return am 93 } 94 95 func (am *Manager) init(keydir string) { 96 am.unlocked = make(map[common.Address]*unlocked) 97 am.cache = newAddrCache(keydir) 98 // TODO: In order for this finalizer to work, there must be no references 99 // to am. addrCache doesn't keep a reference but unlocked keys do, 100 // so the finalizer will not trigger until all timed unlocks have expired. 101 runtime.SetFinalizer(am, func(m *Manager) { 102 m.cache.close() 103 }) 104 } 105 106 // HasAddress reports whether a key with the given address is present. 107 func (am *Manager) HasAddress(addr common.Address) bool { 108 return am.cache.hasAddress(addr) 109 } 110 111 // Accounts returns all key files present in the directory. 112 func (am *Manager) Accounts() []Account { 113 return am.cache.accounts() 114 } 115 116 // Delete deletes the key matched by account if the passphrase is correct. 117 // If the account contains no filename, the address must match a unique key. 118 func (am *Manager) Delete(a Account, passphrase string) error { 119 // Decrypting the key isn't really necessary, but we do 120 // it anyway to check the password and zero out the key 121 // immediately afterwards. 122 a, key, err := am.getDecryptedKey(a, passphrase) 123 if key != nil { 124 zeroKey(key.PrivateKey) 125 } 126 if err != nil { 127 return err 128 } 129 // The order is crucial here. The key is dropped from the 130 // cache after the file is gone so that a reload happening in 131 // between won't insert it into the cache again. 132 err = os.Remove(a.File) 133 if err == nil { 134 am.cache.delete(a) 135 } 136 return err 137 } 138 139 // Sign calculates a ECDSA signature for the given hash. The produced signature 140 // is in the [R || S || V] format where V is 0 or 1. 141 func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) { 142 am.mu.RLock() 143 defer am.mu.RUnlock() 144 145 unlockedKey, found := am.unlocked[addr] 146 if !found { 147 return nil, ErrLocked 148 } 149 return crypto.Sign(hash, unlockedKey.PrivateKey) 150 } 151 152 // SignWithPassphrase signs hash if the private key matching the given address 153 // can be decrypted with the given passphrase. The produced signature is in the 154 // [R || S || V] format where V is 0 or 1. 155 func (am *Manager) SignWithPassphrase(a Account, passphrase string, hash []byte) (signature []byte, err error) { 156 _, key, err := am.getDecryptedKey(a, passphrase) 157 if err != nil { 158 return nil, err 159 } 160 defer zeroKey(key.PrivateKey) 161 return crypto.Sign(hash, key.PrivateKey) 162 } 163 164 // Unlock unlocks the given account indefinitely. 165 func (am *Manager) Unlock(a Account, passphrase string) error { 166 return am.TimedUnlock(a, passphrase, 0) 167 } 168 169 // Lock removes the private key with the given address from memory. 170 func (am *Manager) Lock(addr common.Address) error { 171 am.mu.Lock() 172 if unl, found := am.unlocked[addr]; found { 173 am.mu.Unlock() 174 am.expire(addr, unl, time.Duration(0)*time.Nanosecond) 175 } else { 176 am.mu.Unlock() 177 } 178 return nil 179 } 180 181 // TimedUnlock unlocks the given account with the passphrase. The account 182 // stays unlocked for the duration of timeout. A timeout of 0 unlocks the account 183 // until the program exits. The account must match a unique key file. 184 // 185 // If the account address is already unlocked for a duration, TimedUnlock extends or 186 // shortens the active unlock timeout. If the address was previously unlocked 187 // indefinitely the timeout is not altered. 188 func (am *Manager) TimedUnlock(a Account, passphrase string, timeout time.Duration) error { 189 a, key, err := am.getDecryptedKey(a, passphrase) 190 if err != nil { 191 return err 192 } 193 194 am.mu.Lock() 195 defer am.mu.Unlock() 196 u, found := am.unlocked[a.Address] 197 if found { 198 if u.abort == nil { 199 // The address was unlocked indefinitely, so unlocking 200 // it with a timeout would be confusing. 201 zeroKey(key.PrivateKey) 202 return nil 203 } else { 204 // Terminate the expire goroutine and replace it below. 205 close(u.abort) 206 } 207 } 208 if timeout > 0 { 209 u = &unlocked{Key: key, abort: make(chan struct{})} 210 go am.expire(a.Address, u, timeout) 211 } else { 212 u = &unlocked{Key: key} 213 } 214 am.unlocked[a.Address] = u 215 return nil 216 } 217 218 // Find resolves the given account into a unique entry in the keystore. 219 func (am *Manager) Find(a Account) (Account, error) { 220 am.cache.maybeReload() 221 am.cache.mu.Lock() 222 a, err := am.cache.find(a) 223 am.cache.mu.Unlock() 224 return a, err 225 } 226 227 func (am *Manager) getDecryptedKey(a Account, auth string) (Account, *Key, error) { 228 a, err := am.Find(a) 229 if err != nil { 230 return a, nil, err 231 } 232 key, err := am.keyStore.GetKey(a.Address, a.File, auth) 233 return a, key, err 234 } 235 236 func (am *Manager) expire(addr common.Address, u *unlocked, timeout time.Duration) { 237 t := time.NewTimer(timeout) 238 defer t.Stop() 239 select { 240 case <-u.abort: 241 // just quit 242 case <-t.C: 243 am.mu.Lock() 244 // only drop if it's still the same key instance that dropLater 245 // was launched with. we can check that using pointer equality 246 // because the map stores a new pointer every time the key is 247 // unlocked. 248 if am.unlocked[addr] == u { 249 zeroKey(u.PrivateKey) 250 delete(am.unlocked, addr) 251 } 252 am.mu.Unlock() 253 } 254 } 255 256 // NewAccount generates a new key and stores it into the key directory, 257 // encrypting it with the passphrase. 258 func (am *Manager) NewAccount(passphrase string) (Account, error) { 259 _, account, err := storeNewKey(am.keyStore, crand.Reader, passphrase) 260 if err != nil { 261 return Account{}, err 262 } 263 // Add the account to the cache immediately rather 264 // than waiting for file system notifications to pick it up. 265 am.cache.add(account) 266 return account, nil 267 } 268 269 // AccountByIndex returns the ith account. 270 func (am *Manager) AccountByIndex(i int) (Account, error) { 271 accounts := am.Accounts() 272 if i < 0 || i >= len(accounts) { 273 return Account{}, fmt.Errorf("account index %d out of range [0, %d]", i, len(accounts)-1) 274 } 275 return accounts[i], nil 276 } 277 278 // Export exports as a JSON key, encrypted with newPassphrase. 279 func (am *Manager) Export(a Account, passphrase, newPassphrase string) (keyJSON []byte, err error) { 280 _, key, err := am.getDecryptedKey(a, passphrase) 281 if err != nil { 282 return nil, err 283 } 284 var N, P int 285 if store, ok := am.keyStore.(*keyStorePassphrase); ok { 286 N, P = store.scryptN, store.scryptP 287 } else { 288 N, P = StandardScryptN, StandardScryptP 289 } 290 return EncryptKey(key, newPassphrase, N, P) 291 } 292 293 // Import stores the given encrypted JSON key into the key directory. 294 func (am *Manager) Import(keyJSON []byte, passphrase, newPassphrase string) (Account, error) { 295 key, err := DecryptKey(keyJSON, passphrase) 296 if key != nil && key.PrivateKey != nil { 297 defer zeroKey(key.PrivateKey) 298 } 299 if err != nil { 300 return Account{}, err 301 } 302 return am.importKey(key, newPassphrase) 303 } 304 305 // ImportECDSA stores the given key into the key directory, encrypting it with the passphrase. 306 func (am *Manager) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error) { 307 key := newKeyFromECDSA(priv) 308 if am.cache.hasAddress(key.Address) { 309 return Account{}, fmt.Errorf("account already exists") 310 } 311 312 return am.importKey(key, passphrase) 313 } 314 315 func (am *Manager) importKey(key *Key, passphrase string) (Account, error) { 316 a := Account{Address: key.Address, File: am.keyStore.JoinPath(keyFileName(key.Address))} 317 if err := am.keyStore.StoreKey(a.File, key, passphrase); err != nil { 318 return Account{}, err 319 } 320 am.cache.add(a) 321 return a, nil 322 } 323 324 // Update changes the passphrase of an existing account. 325 func (am *Manager) Update(a Account, passphrase, newPassphrase string) error { 326 a, key, err := am.getDecryptedKey(a, passphrase) 327 if err != nil { 328 return err 329 } 330 return am.keyStore.StoreKey(a.File, key, newPassphrase) 331 } 332 333 // ImportPreSaleKey decrypts the given Ethereum presale wallet and stores 334 // a key file in the key directory. The key file is encrypted with the same passphrase. 335 func (am *Manager) ImportPreSaleKey(keyJSON []byte, passphrase string) (Account, error) { 336 a, _, err := importPreSaleKey(am.keyStore, keyJSON, passphrase) 337 if err != nil { 338 return a, err 339 } 340 am.cache.add(a) 341 return a, nil 342 } 343 344 // zeroKey zeroes a private key in memory. 345 func zeroKey(k *ecdsa.PrivateKey) { 346 b := k.D.Bits() 347 for i := range b { 348 b[i] = 0 349 } 350 }