github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/blockchain/pseudohsm/pseudohsm.go (about) 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 //kdCache map[chainkd.XPub]chainkd.XPrv 38 } 39 40 // XPub type for pubkey for anyone can see 41 type XPub struct { 42 Alias string `json:"alias"` 43 XPub chainkd.XPub `json:"xpub"` 44 File string `json:"file"` 45 } 46 47 // New method for HSM struct 48 func New(keypath string) (*HSM, error) { 49 keydir, _ := filepath.Abs(keypath) 50 return &HSM{ 51 keyStore: &keyStorePassphrase{keydir, LightScryptN, LightScryptP}, 52 cache: newKeyCache(keydir), 53 //kdCache: make(map[chainkd.XPub]chainkd.XPrv), 54 }, nil 55 } 56 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) { 59 h.cacheMu.Lock() 60 defer h.cacheMu.Unlock() 61 62 normalizedAlias := strings.ToLower(strings.TrimSpace(alias)) 63 if ok := h.cache.hasAlias(normalizedAlias); ok { 64 return nil, nil, ErrDuplicateKeyAlias 65 } 66 67 xpub, mnemonic, err := h.createChainKDKey(normalizedAlias, auth, language) 68 if err != nil { 69 return nil, nil, err 70 } 71 h.cache.add(*xpub) 72 return xpub, mnemonic, err 73 } 74 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) { 77 h.cacheMu.Lock() 78 defer h.cacheMu.Unlock() 79 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 84 } 85 86 normalizedAlias := strings.ToLower(strings.TrimSpace(alias)) 87 if ok := h.cache.hasAlias(normalizedAlias); ok { 88 return nil, ErrDuplicateKeyAlias 89 } 90 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 95 } 96 97 xpub, err := h.createKeyFromMnemonic(alias, auth, mnemonic) 98 if err != nil { 99 return nil, err 100 } 101 102 h.cache.add(*xpub) 103 return xpub, nil 104 } 105 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)) 110 if err != nil { 111 return nil, err 112 } 113 id := uuid.NewRandom() 114 key := &XKey{ 115 ID: id, 116 KeyType: "bytom_kd", 117 XPub: xpub, 118 XPrv: xprv, 119 Alias: alias, 120 } 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") 124 } 125 return &XPub{XPub: xpub, Alias: alias, File: file}, nil 126 } 127 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) 131 if err != nil { 132 return nil, nil, err 133 } 134 mnemonic, err := mnem.NewMnemonic(entropy, language) 135 if err != nil { 136 return nil, nil, err 137 } 138 xpub, err := h.createKeyFromMnemonic(alias, auth, mnemonic) 139 if err != nil { 140 return nil, nil, err 141 } 142 return xpub, &mnemonic, nil 143 } 144 145 // UpdateKeyAlias update key alias 146 func (h *HSM) UpdateKeyAlias(xpub chainkd.XPub, newAlias string) error { 147 h.cacheMu.Lock() 148 defer h.cacheMu.Unlock() 149 150 h.cache.maybeReload() 151 h.cache.mu.Lock() 152 xpb, err := h.cache.find(XPub{XPub: xpub}) 153 h.cache.mu.Unlock() 154 if err != nil { 155 return err 156 } 157 158 keyjson, err := ioutil.ReadFile(xpb.File) 159 if err != nil { 160 return err 161 } 162 163 encrptKeyJSON := new(encryptedKeyJSON) 164 if err := json.Unmarshal(keyjson, encrptKeyJSON); err != nil { 165 return err 166 } 167 168 normalizedAlias := strings.ToLower(strings.TrimSpace(newAlias)) 169 if ok := h.cache.hasAlias(normalizedAlias); ok { 170 return ErrDuplicateKeyAlias 171 } 172 173 encrptKeyJSON.Alias = normalizedAlias 174 keyJSON, err := json.Marshal(encrptKeyJSON) 175 if err != nil { 176 return err 177 } 178 179 if err := writeKeyFile(xpb.File, keyJSON); err != nil { 180 return err 181 } 182 183 // update key alias 184 h.cache.delete(xpb) 185 xpb.Alias = normalizedAlias 186 h.cache.add(xpb) 187 188 return nil 189 } 190 191 // ListKeys returns a list of all xpubs from the store 192 func (h *HSM) ListKeys() []XPub { 193 xpubs := h.cache.keys() 194 return xpubs 195 } 196 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) 202 if err != nil { 203 return nil, err 204 } 205 if len(path) > 0 { 206 xprv = xprv.Derive(path) 207 } 208 return xprv.Sign(msg), nil 209 } 210 211 //LoadChainKDKey get xprv from xpub 212 func (h *HSM) LoadChainKDKey(xpub chainkd.XPub, auth string) (xprv chainkd.XPrv, err error) { 213 h.cacheMu.Lock() 214 defer h.cacheMu.Unlock() 215 216 //if xprv, ok := h.kdCache[xpub]; ok { 217 // return xprv, nil 218 //} 219 220 _, xkey, err := h.loadDecryptedKey(xpub, auth) 221 if err != nil { 222 return xprv, ErrLoadKey 223 } 224 //h.kdCache[xpb.XPub] = xkey.XPrv 225 return xkey.XPrv, nil 226 } 227 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. 234 235 xpb, xkey, err := h.loadDecryptedKey(xpub, auth) 236 if xkey != nil { 237 zeroKey(xkey) 238 } 239 if err != nil { 240 return err 241 } 242 243 h.cacheMu.Lock() 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) 248 if err == nil { 249 h.cache.delete(xpb) 250 } 251 h.cacheMu.Unlock() 252 return err 253 } 254 255 func (h *HSM) loadDecryptedKey(xpub chainkd.XPub, auth string) (XPub, *XKey, error) { 256 h.cache.maybeReload() 257 h.cache.mu.Lock() 258 xpb, err := h.cache.find(XPub{XPub: xpub}) 259 260 h.cache.mu.Unlock() 261 if err != nil { 262 return xpb, nil, err 263 } 264 xkey, err := h.keyStore.GetKey(xpb.Alias, xpb.File, auth) 265 return xpb, xkey, err 266 } 267 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) 271 if err != nil { 272 return err 273 } 274 return h.keyStore.StoreKey(xpb.File, xkey, newAuth) 275 } 276 277 // HasAlias check whether the key alias exists 278 func (h *HSM) HasAlias(alias string) bool { 279 return h.cache.hasAlias(alias) 280 } 281 282 // HasKey check whether the private key exists 283 func (h *HSM) HasKey(xprv chainkd.XPrv) bool { 284 return h.cache.hasKey(xprv.XPub()) 285 }