1 // Package pseudohsm provides a pseudo HSM for development environments.
13 "github.com/pborman/uuid"
15 "github.com/bytom/vapor/crypto/ed25519/chainkd"
16 "github.com/bytom/vapor/errors"
17 mnem "github.com/bytom/vapor/wallet/mnemonic"
20 // pre-define errors for supporting bytom errorFormatter
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")
29 // EntropyLength random entropy length to generate mnemonics.
30 const EntropyLength = 128
32 // HSM type for storing pubkey and privatekey
37 //kdCache map[chainkd.XPub]chainkd.XPrv
40 // XPub type for pubkey for anyone can see
42 Alias string `json:"alias"`
43 XPub chainkd.XPub `json:"xpub"`
44 File string `json:"file"`
47 // New method for HSM struct
48 func New(keypath string) (*HSM, error) {
49 keydir, _ := filepath.Abs(keypath)
51 keyStore: &keyStorePassphrase{keydir, LightScryptN, LightScryptP},
52 cache: newKeyCache(keydir),
53 //kdCache: make(map[chainkd.XPub]chainkd.XPrv),
57 // XCreate produces a new random xprv and stores it in the db.
58 func (h *HSM) XCreate(alias string, auth string, language string) (*XPub, *string, error) {
60 defer h.cacheMu.Unlock()
62 normalizedAlias := strings.ToLower(strings.TrimSpace(alias))
63 if ok := h.cache.hasAlias(normalizedAlias); ok {
64 return nil, nil, ErrDuplicateKeyAlias
67 xpub, mnemonic, err := h.createChainKDKey(normalizedAlias, auth, language)
72 return xpub, mnemonic, err
75 // ImportKeyFromMnemonic produces a xprv from mnemonic and stores it in the db.
76 func (h *HSM) ImportKeyFromMnemonic(alias string, auth string, mnemonic string, language string) (*XPub, error) {
78 defer h.cacheMu.Unlock()
80 // checksum length = entropy length /32
81 // mnemonic length = (entropy length + checksum length)/11
82 if len(strings.Fields(mnemonic)) != (EntropyLength+EntropyLength/32)/11 {
83 return nil, ErrMnemonicLength
86 normalizedAlias := strings.ToLower(strings.TrimSpace(alias))
87 if ok := h.cache.hasAlias(normalizedAlias); ok {
88 return nil, ErrDuplicateKeyAlias
91 // Pre validate that the mnemonic is well formed and only contains words that
92 // are present in the word list
93 if !mnem.IsMnemonicValid(mnemonic, language) {
94 return nil, mnem.ErrInvalidMnemonic
97 xpub, err := h.createKeyFromMnemonic(alias, auth, mnemonic)
106 func (h *HSM) createKeyFromMnemonic(alias string, auth string, mnemonic string) (*XPub, error) {
107 // Generate a Bip32 HD wallet for the mnemonic and a user supplied password
108 seed := mnem.NewSeed(mnemonic, "")
109 xprv, xpub, err := chainkd.NewXKeys(bytes.NewBuffer(seed))
113 id := uuid.NewRandom()
121 file := h.keyStore.JoinPath(keyFileName(key.ID.String()))
122 if err := h.keyStore.StoreKey(file, key, auth); err != nil {
123 return nil, errors.Wrap(err, "storing keys")
125 return &XPub{XPub: xpub, Alias: alias, File: file}, nil
128 func (h *HSM) createChainKDKey(alias string, auth string, language string) (*XPub, *string, error) {
129 // Generate a mnemonic for memorization or user-friendly seeds
130 entropy, err := mnem.NewEntropy(EntropyLength)
134 mnemonic, err := mnem.NewMnemonic(entropy, language)
138 xpub, err := h.createKeyFromMnemonic(alias, auth, mnemonic)
142 return xpub, &mnemonic, nil
145 // UpdateKeyAlias update key alias
146 func (h *HSM) UpdateKeyAlias(xpub chainkd.XPub, newAlias string) error {
148 defer h.cacheMu.Unlock()
150 h.cache.maybeReload()
152 xpb, err := h.cache.find(XPub{XPub: xpub})
158 keyjson, err := ioutil.ReadFile(xpb.File)
163 encrptKeyJSON := new(encryptedKeyJSON)
164 if err := json.Unmarshal(keyjson, encrptKeyJSON); err != nil {
168 normalizedAlias := strings.ToLower(strings.TrimSpace(newAlias))
169 if ok := h.cache.hasAlias(normalizedAlias); ok {
170 return ErrDuplicateKeyAlias
173 encrptKeyJSON.Alias = normalizedAlias
174 keyJSON, err := json.Marshal(encrptKeyJSON)
179 if err := writeKeyFile(xpb.File, keyJSON); err != nil {
185 xpb.Alias = normalizedAlias
191 // ListKeys returns a list of all xpubs from the store
192 func (h *HSM) ListKeys() []XPub {
193 xpubs := h.cache.keys()
197 // XSign looks up the xprv given the xpub, optionally derives a new
198 // xprv with the given path (but does not store the new xprv), and
199 // signs the given msg.
200 func (h *HSM) XSign(xpub chainkd.XPub, path [][]byte, msg []byte, auth string) ([]byte, error) {
201 xprv, err := h.LoadChainKDKey(xpub, auth)
206 xprv = xprv.Derive(path)
208 return xprv.Sign(msg), nil
211 //LoadChainKDKey get xprv from xpub
212 func (h *HSM) LoadChainKDKey(xpub chainkd.XPub, auth string) (xprv chainkd.XPrv, err error) {
214 defer h.cacheMu.Unlock()
216 //if xprv, ok := h.kdCache[xpub]; ok {
220 _, xkey, err := h.loadDecryptedKey(xpub, auth)
222 return xprv, ErrLoadKey
224 //h.kdCache[xpb.XPub] = xkey.XPrv
225 return xkey.XPrv, nil
228 // XDelete deletes the key matched by xpub if the passphrase is correct.
229 // If a contains no filename, the address must match a unique key.
230 func (h *HSM) XDelete(xpub chainkd.XPub, auth string) error {
231 // Decrypting the key isn't really necessary, but we do
232 // it anyway to check the password and zero out the key
233 // immediately afterwards.
235 xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
244 // The order is crucial here. The key is dropped from the
245 // cache after the file is gone so that a reload happening in
246 // between won't insert it into the cache again.
247 err = os.Remove(xpb.File)
255 func (h *HSM) loadDecryptedKey(xpub chainkd.XPub, auth string) (XPub, *XKey, error) {
256 h.cache.maybeReload()
258 xpb, err := h.cache.find(XPub{XPub: xpub})
264 xkey, err := h.keyStore.GetKey(xpb.Alias, xpb.File, auth)
265 return xpb, xkey, err
268 // ResetPassword reset passphrase for an existing xpub
269 func (h *HSM) ResetPassword(xpub chainkd.XPub, oldAuth, newAuth string) error {
270 xpb, xkey, err := h.loadDecryptedKey(xpub, oldAuth)
274 return h.keyStore.StoreKey(xpb.File, xkey, newAuth)
277 // HasAlias check whether the key alias exists
278 func (h *HSM) HasAlias(alias string) bool {
279 return h.cache.hasAlias(alias)
282 // HasKey check whether the private key exists
283 func (h *HSM) HasKey(xprv chainkd.XPrv) bool {
284 return h.cache.hasKey(xprv.XPub())