1 // Package pseudohsm provides a pseudo HSM for development environments.
10 "github.com/bytom/crypto/ed25519/chainkd"
11 "github.com/bytom/errors"
12 "github.com/pborman/uuid"
15 // listKeyMaxAliases limits the alias filter to a sane maximum size.
16 const listKeyMaxAliases = 200
19 ErrDuplicateKeyAlias = errors.New("duplicate key alias")
20 ErrInvalidAfter = errors.New("invalid after")
21 ErrNoKey = errors.New("key not found")
22 ErrInvalidKeySize = errors.New("key invalid size")
23 ErrTooManyAliasesToList = errors.New("requested aliases exceeds limit")
24 ErrAmbiguousAlias = errors.New("multiple keys match alias")
25 ErrDecrypt = errors.New("could not decrypt key with given passphrase")
26 ErrInvalidKeyType = errors.New("key type stored invalid")
29 // HSM type for storing pubkey and privatekey
34 kdCache map[chainkd.XPub]chainkd.XPrv
37 // XPub type for pubkey for anyone can see
39 Alias string `json:"alias"`
40 XPub chainkd.XPub `json:"xpub"`
41 File string `json:"file"`
44 // New method for HSM struct
45 func New(keypath string) (*HSM, error) {
46 keydir, _ := filepath.Abs(keypath)
48 keyStore: &keyStorePassphrase{keydir, LightScryptN, LightScryptP},
49 cache: newKeyCache(keydir),
50 kdCache: make(map[chainkd.XPub]chainkd.XPrv),
54 // XCreate produces a new random xprv and stores it in the db.
55 func (h *HSM) XCreate(alias string, auth string) (*XPub, error) {
56 if ok := h.cache.hasAlias(alias); ok {
57 return nil, ErrDuplicateKeyAlias
59 xpub, _, err := h.createChainKDKey(auth, alias, false)
67 func (h *HSM) createChainKDKey(auth string, alias string, get bool) (*XPub, bool, error) {
68 xprv, xpub, err := chainkd.NewXKeys(nil)
70 return nil, false, err
72 id := uuid.NewRandom()
80 file := h.keyStore.JoinPath(keyFileName(key.ID.String()))
81 if err := h.keyStore.StoreKey(file, key, auth); err != nil {
82 return nil, false, errors.Wrap(err, "storing keys")
84 return &XPub{XPub: xpub, Alias: alias, File: file}, true, nil
87 // ListKeys returns a list of all xpubs from the store
88 func (h *HSM) ListKeys(after string, limit int) ([]XPub, string, error) {
90 xpubs := h.cache.keys()
91 start, end := 0, len(xpubs)
99 zafter, err = strconv.Atoi(after)
101 return nil, "", errors.WithDetailf(ErrInvalidAfter, "value: %q", zafter)
105 if len(xpubs) > zafter {
108 return nil, "", errors.WithDetailf(ErrInvalidAfter, "value: %v", zafter)
110 if len(xpubs) > zafter+limit {
113 return xpubs[start:end], strconv.Itoa(start), nil
116 // XSign looks up the xprv given the xpub, optionally derives a new
117 // xprv with the given path (but does not store the new xprv), and
118 // signs the given msg.
119 func (h *HSM) XSign(xpub chainkd.XPub, path [][]byte, msg []byte, auth string) ([]byte, error) {
120 xprv, err := h.loadChainKDKey(xpub, auth)
125 xprv = xprv.Derive(path)
127 return xprv.Sign(msg), nil
130 func (h *HSM) loadChainKDKey(xpub chainkd.XPub, auth string) (xprv chainkd.XPrv, err error) {
132 defer h.cacheMu.Unlock()
134 if xprv, ok := h.kdCache[xpub]; ok {
138 xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
140 return xprv, ErrNoKey
142 h.kdCache[xpb.XPub] = xkey.XPrv
143 return xkey.XPrv, nil
146 // XDelete deletes the key matched by xpub if the passphrase is correct.
147 // If a contains no filename, the address must match a unique key.
148 func (h *HSM) XDelete(xpub chainkd.XPub, auth string) error {
149 // Decrypting the key isn't really necessary, but we do
150 // it anyway to check the password and zero out the key
151 // immediately afterwards.
153 xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
161 // The order is crucial here. The key is dropped from the
162 // cache after the file is gone so that a reload happening in
163 // between won't insert it into the cache again.
164 err = os.Remove(xpb.File)
169 delete(h.kdCache, xpub)
174 func (h *HSM) loadDecryptedKey(xpub chainkd.XPub, auth string) (XPub, *XKey, error) {
175 h.cache.maybeReload()
177 xpb, err := h.cache.find(XPub{XPub: xpub})
183 xkey, err := h.keyStore.GetKey(xpb.Alias, xpb.File, auth)
185 return xpb, xkey, err
188 // ResetPassword the passphrase of an existing xpub
189 func (h *HSM) ResetPassword(xpub chainkd.XPub, auth, newAuth string) error {
190 xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
194 return h.keyStore.StoreKey(xpb.File, xkey, newAuth)