aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/account_manager.go
blob: 2781be656f8930cce82e4af6c6f9ce8ad65ed5a7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// Package implements a private key management facility.
//
// This abstracts part of a user's interaction with an account she controls.
package accounts

// Currently this is pretty much a passthrough to the KeyStore interface,
// and accounts persistence is derived from stored keys' addresses

import (
    "crypto/ecdsa"
    crand "crypto/rand"
    "errors"
    "fmt"
    "os"
    "sync"
    "time"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/crypto"
)

var (
    ErrLocked = errors.New("account is locked")
    ErrNoKeys = errors.New("no keys in store")
)

type Account struct {
    Address common.Address
}

type Manager struct {
    keyStore crypto.KeyStore
    unlocked map[common.Address]*unlocked
    mutex    sync.RWMutex
}

type unlocked struct {
    *crypto.Key
    abort chan struct{}
}

func NewManager(keyStore crypto.KeyStore) *Manager {
    return &Manager{
        keyStore: keyStore,
        unlocked: make(map[common.Address]*unlocked),
    }
}

func (am *Manager) HasAccount(addr common.Address) bool {
    accounts, _ := am.Accounts()
    for _, acct := range accounts {
        if acct.Address == addr {
            return true
        }
    }
    return false
}

func (am *Manager) DeleteAccount(address common.Address, auth string) error {
    return am.keyStore.DeleteKey(address, auth)
}

func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
    am.mutex.RLock()
    defer am.mutex.RUnlock()
    unlockedKey, found := am.unlocked[a.Address]
    if !found {
        return nil, ErrLocked
    }
    signature, err = crypto.Sign(toSign, unlockedKey.PrivateKey)
    return signature, err
}

// Unlock unlocks the given account indefinitely.
func (am *Manager) Unlock(addr common.Address, keyAuth string) error {
    return am.TimedUnlock(addr, keyAuth, 0)
}

// TimedUnlock unlocks the account with the given address. The account
// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
// until the program exits.
//
// If the accout is already unlocked, TimedUnlock extends or shortens
// the active unlock timeout.
func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error {
    key, err := am.keyStore.GetKey(addr, keyAuth)
    if err != nil {
        return err
    }
    var u *unlocked
    am.mutex.Lock()
    defer am.mutex.Unlock()
    var found bool
    u, found = am.unlocked[addr]
    if found {
        // terminate dropLater for this key to avoid unexpected drops.
        if u.abort != nil {
            close(u.abort)
        }
    }
    if timeout > 0 {
        u = &unlocked{Key: key, abort: make(chan struct{})}
        go am.expire(addr, u, timeout)
    } else {
        u = &unlocked{Key: key}
    }
    am.unlocked[addr] = u
    return nil
}

func (am *Manager) expire(addr common.Address, u *unlocked, timeout time.Duration) {
    t := time.NewTimer(timeout)
    defer t.Stop()
    select {
    case <-u.abort:
        // just quit
    case <-t.C:
        am.mutex.Lock()
        // only drop if it's still the same key instance that dropLater
        // was launched with. we can check that using pointer equality
        // because the map stores a new pointer every time the key is
        // unlocked.
        if am.unlocked[addr] == u {
            zeroKey(u.PrivateKey)
            delete(am.unlocked, addr)
        }
        am.mutex.Unlock()
    }
}

func (am *Manager) NewAccount(auth string) (Account, error) {
    key, err := am.keyStore.GenerateNewKey(crand.Reader, auth)
    if err != nil {
        return Account{}, err
    }
    return Account{Address: key.Address}, nil
}

func (am *Manager) AddressByIndex(index int) (addr string, err error) {
    var addrs []common.Address
    addrs, err = am.keyStore.GetKeyAddresses()
    if err != nil {
        return
    }
    if index < 0 || index >= len(addrs) {
        err = fmt.Errorf("index out of range: %d (should be 0-%d)", index, len(addrs)-1)
    } else {
        addr = addrs[index].Hex()
    }
    return
}

func (am *Manager) Accounts() ([]Account, error) {
    addresses, err := am.keyStore.GetKeyAddresses()
    if os.IsNotExist(err) {
        return nil, ErrNoKeys
    } else if err != nil {
        return nil, err
    }
    accounts := make([]Account, len(addresses))
    for i, addr := range addresses {
        accounts[i] = Account{
            Address: addr,
        }
    }
    return accounts, err
}

// zeroKey zeroes a private key in memory.
func zeroKey(k *ecdsa.PrivateKey) {
    b := k.D.Bits()
    for i := range b {
        b[i] = 0
    }
}

// USE WITH CAUTION = this will save an unencrypted private key on disk
// no cli or js interface
func (am *Manager) Export(path string, addr common.Address, keyAuth string) error {
    key, err := am.keyStore.GetKey(addr, keyAuth)
    if err != nil {
        return err
    }
    return crypto.SaveECDSA(path, key.PrivateKey)
}

func (am *Manager) Import(path string, keyAuth string) (Account, error) {
    privateKeyECDSA, err := crypto.LoadECDSA(path)
    if err != nil {
        return Account{}, err
    }
    key := crypto.NewKeyFromECDSA(privateKeyECDSA)
    if err = am.keyStore.StoreKey(key, keyAuth); err != nil {
        return Account{}, err
    }
    return Account{Address: key.Address}, nil
}

func (am *Manager) Update(addr common.Address, authFrom, authTo string) (err error) {
    var key *crypto.Key
    key, err = am.keyStore.GetKey(addr, authFrom)

    if err == nil {
        err = am.keyStore.StoreKey(key, authTo)
        if err == nil {
            am.keyStore.Cleanup(addr)
        }
    }
    return
}

func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) {
    var key *crypto.Key
    key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password)
    if err != nil {
        return
    }
    return Account{Address: key.Address}, nil
}
d>2004-05-041-25/+1 * Update lang/ruby18 and the bundled modules to the latest 1.8 branchAkinori MUSHA2004-05-035-18/+73 * Update to 8.0.066.Alexander Leidinger2004-04-302-5/+3 * Update to 1.2.3Tilman Keskinoz2004-04-292-4/+3 * Add libjit 0.0.0f,Kirill Ponomarev2004-04-299-0/+107 * - Update to version 8.1.2Kirill Ponomarev2004-04-294-22/+12 * - patch to fix coredumps in make_servicesDirk Meyer2004-04-292-1/+30 * Update to 5.0.0RC2 release (stop packaging customized snapshots,Alex Dupre2004-04-2913-61/+42 * Fix plist.Volker Stolz2004-04-291-0/+1 * - Add mirror to fix build for releaseClement Laforet2004-04-221-3/+3 * - Fix IGNORE messageKirill Ponomarev2004-04-202-2/+2 * - Mark IGNORE on alphaKirill Ponomarev2004-04-201-0/+4 * - Mark IGNORE on alphaKirill Ponomarev2004-04-201-0/+4 * BROKEN on amd64: Does not compileKris Kennaway2004-04-191-1/+7 * These broken ports are scheduled for deletion on June 18 if they areKris Kennaway2004-04-191-0/+2 * o Update to 1.8.Norikatsu Shigemura2004-04-192-8/+8 * - Update to 1.3.0.20040413Pav Lucistnik2004-04-193-9/+7 * Mark DEPRECATED. These ports are quite old and useless in theseHye-Shik Chang2004-04-182-0/+2 * Reduce configure warning.Hye-Shik Chang2004-04-1810-0/+10 * Update lang/ruby16 to the latest snapshot as of 2004-04-16, whichAkinori MUSHA2004-04-181-0/+2 * Update to the 2004-04-11 snapshot and a later version of the bounds checkingGerald Pfeifer2004-04-182-15/+18 * Update to the 2004-04-14 snapshot, which should be nearly identical to theGerald Pfeifer2004-04-1826-182/+221 * - Fix plistKirill Ponomarev2004-04-171-0/+2 * - fix typo in condition.Dirk Meyer2004-04-161-1/+1 * Update to 4.3.6 release.Alex Dupre2004-04-163-4/+47 * - backward compatibility option GNUSTEP_WITH_GCC32=yesDirk Meyer2004-04-161-0/+12 * - update to gcc33Dirk Meyer2004-04-161-6/+4 * - fix amd64 buildDirk Meyer2004-04-161-5/+17 * Add missing manpageKris Kennaway2004-04-151-1/+1 * Add missing @dirrm and correct syntax errors in the NOPORTDOCS caseKris Kennaway2004-04-152-6/+8 * Mark as deprecated: its dependendencies are broken, and it relies onMark Linimon2004-04-151-0/+2 * Fixed a bug where the configure script couldn't detect getaddrinfo()Hye-Shik Chang2004-04-148-7/+41 * Info files cannot be installed in post-installKris Kennaway2004-04-141-1/+1 * Add missing @dirrmKris Kennaway2004-04-141-0/+1 * Add missing symlinkKris Kennaway2004-04-131-0/+1 * slave port of gcc32Dirk Meyer2004-04-132-0/+31 * - resurrect from 2003/03/02Dirk Meyer2004-04-133-0/+27 * - resurrect from 2003/03/02Dirk Meyer2004-04-131-22/+22 * - take maintainershipDirk Meyer2004-04-131-2/+2 * - Update to 1.8.8Kirill Ponomarev2004-04-132-6/+6 * Install the source instead of the cat-file in ${PREFIX}/man/man1Jean-Marc Zucconi2004-04-132-23/+25 * Add fpc-base 1.0.10, meta package to install all of theThierry Thomas2004-04-134-0/+53 * Add fpc-demo 1.0.10, Free Pascal compiler examples.Thierry Thomas2004-04-135-0/+142 * Per kris' suggestion, mark as deprecated (supplanted by lang/tclX whichMark Linimon2004-04-121-0/+3 * Per distfile survey, remove mastersite that disappeared in December 2003.Mark Linimon2004-04-121-1/+1 * BROKEN on sparc64: Does not buildKris Kennaway2004-04-121-1/+7 * BROKEN on sparc64: Does not compileKris Kennaway2004-04-121-1/+7 * BROKEN on sparc64 as wellKris Kennaway2004-04-121-2/+2 * BROKEN on sparc64: Does not compileKris Kennaway2004-04-121-1/+7 * Remove duplicate listing of ${MOZART_DOCS} in ${DISTFILES} to unbreak theKris Kennaway2004-04-122-3/+1 * This old version of GCC really only makes sense on i386 at this point (if atGerald Pfeifer2004-04-111-5/+3 * The tarball of 0.92 has been replaced by 0.92a.Thierry Thomas2004-04-113-29/+5 * Tidy up whitespace.Trevor Johnson2004-04-1114-32/+32 * Cram into 80 columns by 24 rows.Trevor Johnson2004-04-113-53/+35 * Trim whitespace.Trevor Johnson2004-04-111-1/+0 * - Update to 4.1.1Pav Lucistnik2004-04-107-161/+46 * Fix an horrible bug in Gambas.Thierry Thomas2004-04-102-0/+23 * updated to 8.0.039.p044.1Maho Nakata2004-04-093-188/+206 * 1) update to 7.1.040Maho Nakata2004-04-09