github.com/cranelv/ethereum_mpc@v0.0.0-20191031014521-23aeb1415092/accounts/keystore/mpcKeyStore.go (about) 1 package keystore 2 3 import ( 4 "math/big" 5 "github.com/pborman/uuid" 6 "github.com/ethereum/go-ethereum/common" 7 "crypto/ecdsa" 8 "github.com/ethereum/go-ethereum/crypto" 9 "github.com/ethereum/go-ethereum/accounts" 10 "crypto/aes" 11 "encoding/hex" 12 "encoding/json" 13 "github.com/ethereum/go-ethereum/crypto/randentropy" 14 "golang.org/x/crypto/scrypt" 15 "github.com/ethereum/go-ethereum/common/math" 16 "errors" 17 "sync" 18 "path/filepath" 19 "io/ioutil" 20 "github.com/ethereum/go-ethereum/log" 21 "github.com/ethereum/go-ethereum/crypto/secp256k1" 22 ) 23 var MpcKeyStoreScheme = "mpcKeystore" 24 type MpcKey struct { 25 Id uuid.UUID // Version 4 "random" for unique id not derived from key data 26 // to simplify lookups we also store the address 27 Address common.MpcAddress 28 // we only store privkey as pubkey/address can be derived from it 29 // privkey in this struct is always in plaintext 30 PrivateKey *big.Int 31 //MPC 32 MPCSeed uint64 33 Quorum uint64 34 MPCGroup []uint64 35 36 } 37 type encryptedMpcKeyJSONV3 struct { 38 Address string `json:"address"` 39 Crypto cryptoJSON `json:"crypto"` 40 Id string `json:"id"` 41 MPCSeed uint64 `json:"mpcseed"` 42 Quorum uint64 `json:"quorum"` 43 MPCGroup []uint64 `json:"mpcGroup"` 44 Version int `json:"version"` 45 } 46 47 // EncryptKey encrypts a key using the specified scrypt parameters into a json 48 // blob that can be decrypted later on. 49 func (key* MpcKey)EncryptKey(auth string, scryptN, scryptP int) ([]byte, error) { 50 authArray := []byte(auth) 51 salt := randentropy.GetEntropyCSPRNG(32) 52 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) 53 if err != nil { 54 return nil, err 55 } 56 encryptKey := derivedKey[:16] 57 keyBytes := math.PaddedBigBytes(key.PrivateKey, 32) 58 59 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 60 cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 61 if err != nil { 62 return nil, err 63 } 64 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 65 66 scryptParamsJSON := make(map[string]interface{}, 5) 67 scryptParamsJSON["n"] = scryptN 68 scryptParamsJSON["r"] = scryptR 69 scryptParamsJSON["p"] = scryptP 70 scryptParamsJSON["dklen"] = scryptDKLen 71 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 72 73 cipherParamsJSON := cipherparamsJSON{ 74 IV: hex.EncodeToString(iv), 75 } 76 77 cryptoStruct := cryptoJSON{ 78 Cipher: "aes-128-ctr", 79 CipherText: hex.EncodeToString(cipherText), 80 CipherParams: cipherParamsJSON, 81 KDF: keyHeaderKDF, 82 KDFParams: scryptParamsJSON, 83 MAC: hex.EncodeToString(mac), 84 } 85 encryptedKeyJSONV3 := encryptedMpcKeyJSONV3{ 86 hex.EncodeToString(key.Address[:]), 87 cryptoStruct, 88 key.Id.String(), 89 key.MPCSeed, 90 key.Quorum, 91 key.MPCGroup, 92 version, 93 } 94 return json.Marshal(encryptedKeyJSONV3) 95 } 96 func PubkeyToMpcAddress(p ecdsa.PublicKey) common.MpcAddress{ 97 var addr common.MpcAddress 98 copy(addr[:],secp256k1.CompressPubkey(p.X,p.Y)) 99 return addr 100 } 101 func newMpcKey(pKey *ecdsa.PublicKey, pShare *big.Int, seed uint64,quorum uint64,MPCGroup[]uint64) (*MpcKey, error) { 102 priv := new(ecdsa.PrivateKey) 103 priv.PublicKey.Curve = crypto.S256() 104 priv.D = pShare 105 priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(pShare.Bytes()) 106 id := uuid.NewRandom() 107 108 key := &MpcKey{ 109 Id: id, 110 // Address: common.BytesToHash()secp256k1.CompressPubkey(pKey.X,pKey.Y), 111 PrivateKey: pShare, 112 MPCSeed:seed, 113 Quorum:quorum, 114 MPCGroup:MPCGroup, 115 } 116 copy(key.Address[:],secp256k1.CompressPubkey(pKey.X,pKey.Y)) 117 //3 bytes for every seed 118 return key , nil 119 } 120 // DecryptKey decrypts a key from a json blob, returning the private key itself. 121 func DecryptMpcKey(keyjson []byte, auth string) (*MpcKey, error) { 122 // Parse the json into a simple map to fetch the key version 123 m := make(map[string]interface{}) 124 if err := json.Unmarshal(keyjson, &m); err != nil { 125 return nil, err 126 } 127 // Depending on the version try to parse one way or another 128 var ( 129 keyBytes, keyId []byte 130 err error 131 ) 132 if version, ok := m["version"].(string); ok && version != "3" { 133 return nil,errors.New("version must equal 3") 134 } 135 k := new(encryptedMpcKeyJSONV3) 136 if err := json.Unmarshal(keyjson, k); err != nil { 137 return nil, err 138 } 139 k1 := encryptedKeyJSONV3{ 140 k.Address, 141 k.Crypto, 142 k.Id, 143 k.Version, 144 } 145 keyBytes, keyId, err = decryptKeyV3(&k1, auth) 146 // Handle any decryption errors and return the key 147 if err != nil { 148 return nil, err 149 } 150 key := crypto.ToECDSAUnsafe(keyBytes) 151 152 mpcKey := MpcKey{ 153 Id: uuid.UUID(keyId), 154 // Address: common.HexToAddress(k.Address), 155 PrivateKey: key.D, 156 MPCSeed : k.MPCSeed, 157 Quorum: k.Quorum, 158 MPCGroup:k.MPCGroup, 159 } 160 copy(mpcKey.Address[:],common.FromHex(k.Address)) 161 return &mpcKey,nil 162 } 163 //cranelv add storemanKey 164 func storeMpcKey(ks keyStore, pKey *ecdsa.PublicKey, pShare *big.Int, seed uint64,quorum uint64,MPCGroup []uint64, passphrase string)(*MpcKey,accounts.MpcAccount, error){ 165 key, err := newMpcKey(pKey, pShare, seed,quorum,MPCGroup) 166 if err!= nil { 167 return nil,accounts.MpcAccount{}, err 168 } 169 170 a := accounts.MpcAccount{key.Address,accounts.URL{Scheme: MpcKeyStoreScheme, Path: ks.JoinPath(mpcKeyFileName(key.Address))}} 171 if err := ks.StoreMpcKey(a.URL.Path, key, passphrase); err != nil { 172 key.PrivateKey = big.NewInt(0) 173 return nil,a, err 174 } 175 176 return key,a, err 177 } 178 type MpcKeyStore struct { 179 mu sync.RWMutex 180 keyDir string 181 storage keyStore // Storage backend, might be cleartext or encrypted 182 keyStoreFiles map[common.MpcAddress] string 183 unlocked map[common.MpcAddress]*MpcKey // Currently unlocked account (decrypted private keys) 184 } 185 func (mks* MpcKeyStore) UnlockAddress(address common.MpcAddress,password string)error{ 186 mks.mu.Lock() 187 defer mks.mu.Unlock() 188 if _,exist := mks.unlocked[address];exist{ 189 return nil 190 } 191 if file,exist := mks.keyStoreFiles[address];!exist{ 192 return ErrNoMatch 193 }else{ 194 key,err := mks.storage.GetMpcKey(address,file,password) 195 if err != nil{ 196 return err 197 } 198 mks.unlocked[address] = key 199 return nil 200 } 201 } 202 func (mks* MpcKeyStore) init(keydir string){ 203 mks.keyDir = keydir 204 err := mks.scan() 205 if err != nil{ 206 log.Error("MpcKeyStore init Error","error",err) 207 } 208 } 209 210 // scan performs a new scan on the given directory, compares against the already 211 // cached filenames, and returns file sets: creates, deletes, updates. 212 func (mks* MpcKeyStore) scan() error { 213 214 // List all the failes from the keystore folder 215 files, err := ioutil.ReadDir(mks.keyDir) 216 if err != nil { 217 return err 218 } 219 220 for _, fi := range files { 221 // Skip any non-key files from the folder 222 path := filepath.Join(mks.keyDir, fi.Name()) 223 if skipKeyFile(fi) { 224 log.Trace("Ignoring file on account scan", "path", path) 225 continue 226 } 227 keyjson, err := ioutil.ReadFile(path) 228 if err != nil { 229 return err 230 } 231 k := new(encryptedMpcKeyJSONV3) 232 if err := json.Unmarshal(keyjson, k); err != nil { 233 return err 234 } 235 mks.keyStoreFiles[common.HexToMpcAddress(k.Address)] = path 236 } 237 return nil 238 } 239 240 func (mks* MpcKeyStore) GetMpcKey(address common.MpcAddress)*MpcKey{ 241 mks.mu.RLock() 242 defer mks.mu.RUnlock() 243 return mks.unlocked[address] 244 } 245 func (mks* MpcKeyStore)StoreMpcKey(pKey *ecdsa.PublicKey, pShare *big.Int, seed uint64,quorum uint64,mpcGroup []uint64, passphrase string)(common.MpcAddress,error){ 246 _,acc,err := storeMpcKey(mks.storage,pKey,pShare,seed,quorum,mpcGroup,passphrase) 247 if err != nil{ 248 return common.MpcAddress{},err 249 } 250 mks.mu.Lock() 251 defer mks.mu.Unlock() 252 mks.keyStoreFiles[acc.Address] = acc.URL.Path 253 return acc.Address,nil 254 } 255 func NewMpcKeyStore(keydir string) *MpcKeyStore { 256 keydir, _ = filepath.Abs(keydir) 257 ks := &MpcKeyStore{storage: &keyStorePassphrase{keydir, LightScryptN, LightScryptP}, 258 keyStoreFiles:make(map[common.MpcAddress] string), 259 unlocked:make(map[common.MpcAddress]*MpcKey)} 260 ks.init(keydir) 261 return ks 262 }