OSDN Git Service

delete useless code (#1960)
[bytom/bytom.git] / blockchain / pseudohsm / pseudohsm.go
1 // Package pseudohsm provides a pseudo HSM for development environments.
2 package pseudohsm
3
4 import (
5         "bytes"
6         "encoding/json"
7         "io/ioutil"
8         "os"
9         "path/filepath"
10         "strings"
11         "sync"
12
13         "github.com/pborman/uuid"
14
15         "github.com/bytom/bytom/crypto/ed25519/chainkd"
16         "github.com/bytom/bytom/errors"
17         mnem "github.com/bytom/bytom/wallet/mnemonic"
18 )
19
20 // pre-define errors for supporting bytom errorFormatter
21 var (
22         ErrDuplicateKeyAlias = errors.New("duplicate key alias")
23         ErrXPubFormat        = errors.New("xpub format error")
24         ErrLoadKey           = errors.New("key not found or wrong password ")
25         ErrDecrypt           = errors.New("could not decrypt key with given passphrase")
26         ErrMnemonicLength    = errors.New("mnemonic length error")
27 )
28
29 // EntropyLength random entropy length to generate mnemonics.
30 const EntropyLength = 128
31
32 // HSM type for storing pubkey and privatekey
33 type HSM struct {
34         cacheMu  sync.Mutex
35         keyStore keyStore
36         cache    *keyCache
37 }
38
39 // XPub type for pubkey for anyone can see
40 type XPub struct {
41         Alias string       `json:"alias"`
42         XPub  chainkd.XPub `json:"xpub"`
43         File  string       `json:"file"`
44 }
45
46 // New method for HSM struct
47 func New(keypath string) (*HSM, error) {
48         keydir, _ := filepath.Abs(keypath)
49         return &HSM{
50                 keyStore: &keyStorePassphrase{keydir, LightScryptN, LightScryptP},
51                 cache:    newKeyCache(keydir),
52         }, nil
53 }
54
55 // XCreate produces a new random xprv and stores it in the db.
56 func (h *HSM) XCreate(alias string, auth string, language string) (*XPub, *string, error) {
57         h.cacheMu.Lock()
58         defer h.cacheMu.Unlock()
59
60         normalizedAlias := strings.ToLower(strings.TrimSpace(alias))
61         if ok := h.cache.hasAlias(normalizedAlias); ok {
62                 return nil, nil, ErrDuplicateKeyAlias
63         }
64
65         xpub, mnemonic, err := h.createChainKDKey(normalizedAlias, auth, language)
66         if err != nil {
67                 return nil, nil, err
68         }
69         h.cache.add(*xpub)
70         return xpub, mnemonic, err
71 }
72
73 // ImportKeyFromMnemonic produces a xprv from mnemonic and stores it in the db.
74 func (h *HSM) ImportKeyFromMnemonic(alias string, auth string, mnemonic string, language string) (*XPub, error) {
75         h.cacheMu.Lock()
76         defer h.cacheMu.Unlock()
77
78         // checksum length = entropy length /32
79         // mnemonic length = (entropy length + checksum length)/11
80         if len(strings.Fields(mnemonic)) != (EntropyLength+EntropyLength/32)/11 {
81                 return nil, ErrMnemonicLength
82         }
83
84         normalizedAlias := strings.ToLower(strings.TrimSpace(alias))
85         if ok := h.cache.hasAlias(normalizedAlias); ok {
86                 return nil, ErrDuplicateKeyAlias
87         }
88
89         // Pre validate that the mnemonic is well formed and only contains words that
90         // are present in the word list
91         if !mnem.IsMnemonicValid(mnemonic, language) {
92                 return nil, mnem.ErrInvalidMnemonic
93         }
94
95         xpub, err := h.createKeyFromMnemonic(alias, auth, mnemonic)
96         if err != nil {
97                 return nil, err
98         }
99
100         h.cache.add(*xpub)
101         return xpub, nil
102 }
103
104 func (h *HSM) createKeyFromMnemonic(alias string, auth string, mnemonic string) (*XPub, error) {
105         // Generate a Bip32 HD wallet for the mnemonic and a user supplied password
106         seed := mnem.NewSeed(mnemonic, "")
107         xprv, xpub, err := chainkd.NewXKeys(bytes.NewBuffer(seed))
108         if err != nil {
109                 return nil, err
110         }
111         id := uuid.NewRandom()
112         key := &XKey{
113                 ID:      id,
114                 KeyType: "bytom_kd",
115                 XPub:    xpub,
116                 XPrv:    xprv,
117                 Alias:   alias,
118         }
119         file := h.keyStore.JoinPath(keyFileName(key.ID.String()))
120         if err := h.keyStore.StoreKey(file, key, auth); err != nil {
121                 return nil, errors.Wrap(err, "storing keys")
122         }
123         return &XPub{XPub: xpub, Alias: alias, File: file}, nil
124 }
125
126 func (h *HSM) createChainKDKey(alias string, auth string, language string) (*XPub, *string, error) {
127         // Generate a mnemonic for memorization or user-friendly seeds
128         entropy, err := mnem.NewEntropy(EntropyLength)
129         if err != nil {
130                 return nil, nil, err
131         }
132         mnemonic, err := mnem.NewMnemonic(entropy, language)
133         if err != nil {
134                 return nil, nil, err
135         }
136         xpub, err := h.createKeyFromMnemonic(alias, auth, mnemonic)
137         if err != nil {
138                 return nil, nil, err
139         }
140         return xpub, &mnemonic, nil
141 }
142
143 // UpdateKeyAlias update key alias
144 func (h *HSM) UpdateKeyAlias(xpub chainkd.XPub, newAlias string) error {
145         h.cacheMu.Lock()
146         defer h.cacheMu.Unlock()
147
148         h.cache.maybeReload()
149         h.cache.mu.Lock()
150         xpb, err := h.cache.find(XPub{XPub: xpub})
151         h.cache.mu.Unlock()
152         if err != nil {
153                 return err
154         }
155
156         keyjson, err := ioutil.ReadFile(xpb.File)
157         if err != nil {
158                 return err
159         }
160
161         encrptKeyJSON := new(encryptedKeyJSON)
162         if err := json.Unmarshal(keyjson, encrptKeyJSON); err != nil {
163                 return err
164         }
165
166         normalizedAlias := strings.ToLower(strings.TrimSpace(newAlias))
167         if ok := h.cache.hasAlias(normalizedAlias); ok {
168                 return ErrDuplicateKeyAlias
169         }
170
171         encrptKeyJSON.Alias = normalizedAlias
172         keyJSON, err := json.Marshal(encrptKeyJSON)
173         if err != nil {
174                 return err
175         }
176
177         if err := writeKeyFile(xpb.File, keyJSON); err != nil {
178                 return err
179         }
180
181         // update key alias
182         h.cache.delete(xpb)
183         xpb.Alias = normalizedAlias
184         h.cache.add(xpb)
185
186         return nil
187 }
188
189 // ListKeys returns a list of all xpubs from the store
190 func (h *HSM) ListKeys() []XPub {
191         xpubs := h.cache.keys()
192         return xpubs
193 }
194
195 // XSign looks up the xprv given the xpub, optionally derives a new
196 // xprv with the given path (but does not store the new xprv), and
197 // signs the given msg.
198 func (h *HSM) XSign(xpub chainkd.XPub, path [][]byte, msg []byte, auth string) ([]byte, error) {
199         xprv, err := h.LoadChainKDKey(xpub, auth)
200         if err != nil {
201                 return nil, err
202         }
203         if len(path) > 0 {
204                 xprv = xprv.Derive(path)
205         }
206         return xprv.Sign(msg), nil
207 }
208
209 //LoadChainKDKey get xprv from xpub
210 func (h *HSM) LoadChainKDKey(xpub chainkd.XPub, auth string) (xprv chainkd.XPrv, err error) {
211         h.cacheMu.Lock()
212         defer h.cacheMu.Unlock()
213
214         _, xkey, err := h.loadDecryptedKey(xpub, auth)
215         if err != nil {
216                 return xprv, ErrLoadKey
217         }
218
219         return xkey.XPrv, nil
220 }
221
222 // XDelete deletes the key matched by xpub if the passphrase is correct.
223 // If a contains no filename, the address must match a unique key.
224 func (h *HSM) XDelete(xpub chainkd.XPub, auth string) error {
225         // Decrypting the key isn't really necessary, but we do
226         // it anyway to check the password and zero out the key
227         // immediately afterwards.
228
229         xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
230         if xkey != nil {
231                 zeroKey(xkey)
232         }
233         if err != nil {
234                 return err
235         }
236
237         h.cacheMu.Lock()
238         // The order is crucial here. The key is dropped from the
239         // cache after the file is gone so that a reload happening in
240         // between won't insert it into the cache again.
241         err = os.Remove(xpb.File)
242         if err == nil {
243                 h.cache.delete(xpb)
244         }
245         h.cacheMu.Unlock()
246         return err
247 }
248
249 func (h *HSM) loadDecryptedKey(xpub chainkd.XPub, auth string) (XPub, *XKey, error) {
250         h.cache.maybeReload()
251         h.cache.mu.Lock()
252         xpb, err := h.cache.find(XPub{XPub: xpub})
253
254         h.cache.mu.Unlock()
255         if err != nil {
256                 return xpb, nil, err
257         }
258         xkey, err := h.keyStore.GetKey(xpb.Alias, xpb.File, auth)
259         return xpb, xkey, err
260 }
261
262 // ResetPassword reset passphrase for an existing xpub
263 func (h *HSM) ResetPassword(xpub chainkd.XPub, oldAuth, newAuth string) error {
264         xpb, xkey, err := h.loadDecryptedKey(xpub, oldAuth)
265         if err != nil {
266                 return err
267         }
268         return h.keyStore.StoreKey(xpb.File, xkey, newAuth)
269 }
270
271 // HasAlias check whether the key alias exists
272 func (h *HSM) HasAlias(alias string) bool {
273         return h.cache.hasAlias(alias)
274 }
275
276 // HasKey check whether the private key exists
277 func (h *HSM) HasKey(xprv chainkd.XPrv) bool {
278         return h.cache.hasKey(xprv.XPub())
279 }