OSDN Git Service

add wallet
[bytom/bytom.git] / blockchain / pseudohsm / pseudohsm.go
1 // Package pseudohsm provides a pseudo HSM for development environments.
2 package pseudohsm
3
4 import (
5         //"context"
6         _"fmt"
7         "strconv"
8         "path/filepath"
9         "sync"
10         "os"
11
12         "bytom/crypto/ed25519/chainkd"
13         "bytom/common"
14         "bytom/errors"
15         "bytom/crypto"
16         //"bytom/protocol/bc/legacy"
17
18         "bytom/blockchain/config"
19         "github.com/pborman/uuid"
20 )
21
22 // listKeyMaxAliases limits the alias filter to a sane maximum size.
23 const listKeyMaxAliases = 200
24
25 var (
26         ErrInvalidAfter         = errors.New("invalid after")
27         ErrNoKey                = errors.New("key not found")
28         ErrInvalidKeySize       = errors.New("key invalid size")
29         ErrTooManyAliasesToList = errors.New("requested aliases exceeds limit")
30         ErrAmbiguousAddr                = errors.New("multiple keys match address")
31         ErrDecrypt                              = errors.New("could not decrypt key with given passphrase")
32         ErrInvalidKeyType               = errors.New("key type stored invalid")
33 )
34
35 type HSM struct {
36         cacheMu  sync.Mutex
37         keyStore keyStore
38         cache    *addrCache
39         kdCache  map[chainkd.XPub]chainkd.XPrv
40 }
41
42 type XPub struct {
43         Alias    string            `json:"alias"`
44         Address common.Address `json:"address"`
45         XPub  chainkd.XPub         `json:"xpub"`
46         File    string             `json:"file"`
47 }
48
49 func New(conf *config.Config) *HSM {
50         keydir, _ := filepath.Abs(conf.KeyPath)
51         return &HSM{
52                 keyStore:   &keyStorePassphrase{keydir, LightScryptN, LightScryptP},
53                 cache:          newAddrCache(keydir),
54                 kdCache:        make(map[chainkd.XPub]chainkd.XPrv),
55         }
56 }
57
58 // XCreate produces a new random xprv and stores it in the db.
59 func (h *HSM) XCreate(auth string, alias string) (*XPub, error) {
60         xpub, _, err := h.createChainKDKey(auth, alias, false)
61         if err != nil {
62                 return nil, err
63         }
64         h.cache.add(*xpub)
65         return xpub, err
66 }
67
68 func (h *HSM) createChainKDKey(auth string, alias string, get bool) (*XPub, bool, error) {
69         xprv, xpub, err := chainkd.NewXKeys(nil)
70         if err != nil {
71                 return nil, false, err
72         }
73         id := uuid.NewRandom()
74         key := &XKey{
75                 Id: id, 
76                 KeyType: "bytom_kd", 
77                 Address: crypto.PubkeyToAddress(xpub[:]),
78                 XPub: xpub, 
79                 XPrv: xprv,
80                 Alias: alias,
81         }
82         file := h.keyStore.JoinPath(keyFileName(key.Address))
83         if err := h.keyStore.StoreKey(file, key, auth); err != nil {
84                 return nil, false, errors.Wrap(err, "storing keys")
85         }
86         return &XPub{XPub: xpub, Address: key.Address, Alias: alias, File: file}, true, nil
87 }
88
89
90 // ListKeys returns a list of all xpubs from the store
91 func (h *HSM) ListKeys(after int , limit int) ([]XPub, string, error) {
92
93         xpubs := h.cache.keys()
94         start, end := 0, len(xpubs)
95         if len(xpubs) > after {
96                 start = after
97         } else {
98                 return nil, "", errors.WithDetailf(ErrInvalidAfter, "value: %v", after)
99         }
100         if len(xpubs) > after+limit {
101                 end = after+limit
102         }
103         return xpubs[start:end], strconv.FormatInt(int64(start), 10), nil
104 }
105
106 // XSign looks up the xprv given the xpub, optionally derives a new
107 // xprv with the given path (but does not store the new xprv), and
108 // signs the given msg.
109 func (h *HSM) XSign(xpub chainkd.XPub, path [][]byte, msg []byte, auth string) ([]byte, error) {
110         xprv, err := h.loadChainKDKey(xpub, auth)
111         if err != nil {
112                 return nil, err
113         }
114         if len(path) > 0 {
115                 xprv = xprv.Derive(path)
116         }
117         return xprv.Sign(msg), nil
118 }
119
120 func (h *HSM) loadChainKDKey(xpub chainkd.XPub, auth string) (xprv chainkd.XPrv, err error) {
121         h.cacheMu.Lock()
122         defer h.cacheMu.Unlock()
123
124         if xprv, ok := h.kdCache[xpub]; ok {
125                 return xprv, nil
126         }
127
128         xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
129         if err != nil {
130                 return xprv, ErrNoKey
131         }
132         h.kdCache[xpb.XPub] = xkey.XPrv
133         return xkey.XPrv, nil
134 }
135
136
137 // XDelete deletes the key matched by xpub if the passphrase is correct.
138 // If a contains no filename, the address must match a unique key.
139 func (h *HSM) XDelete(xpub chainkd.XPub, auth string) error {
140         // Decrypting the key isn't really necessary, but we do
141         // it anyway to check the password and zero out the key
142         // immediately afterwards.
143         
144         xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
145         if xkey != nil {
146                 zeroKey(xkey)
147         }
148         if err != nil {
149                 return err
150         }
151
152         // The order is crucial here. The key is dropped from the
153         // cache after the file is gone so that a reload happening in
154         // between won't insert it into the cache again.
155         err = os.Remove(xpb.File)
156         if err == nil {
157                 h.cache.delete(xpb)
158         }
159         h.cacheMu.Lock()
160         delete(h.kdCache, xpub)
161         h.cacheMu.Unlock()
162         return err
163 }
164
165 func (h *HSM) loadDecryptedKey(xpub chainkd.XPub, auth string) (XPub, *XKey, error) {
166         h.cache.maybeReload()
167         h.cache.mu.Lock()
168         xpb, err := h.cache.find(XPub{XPub: xpub, Address: crypto.PubkeyToAddress(xpub[:])})
169
170         h.cache.mu.Unlock()
171         if err != nil {
172                 return xpb, nil, err
173         }
174         xkey, err := h.keyStore.GetKey(xpb.Address, xpb.File, auth)
175
176         return xpb, xkey, err
177 }
178
179 // Update alias  of an existing xpub
180 func (h *HSM) UpdateAlias(xpub chainkd.XPub, auth, newAlias string) error {
181         xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
182         if err != nil {
183                 return err
184         }
185         xkey.Alias = newAlias
186         return h.keyStore.StoreKey(xpb.File, xkey, auth)
187 }
188
189 // Update changes the passphrase of an existing xpub
190 func (h *HSM) ResetPassword(xpub chainkd.XPub, auth, newAuth string) error {
191         xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
192         if err != nil {
193                 return err
194         }
195         return h.keyStore.StoreKey(xpb.File, xkey, newAuth)
196 }