github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/accounts/keystore/key.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:31</date> 10 //</624450064198144000> 11 12 13 package keystore 14 15 import ( 16 "bytes" 17 "crypto/ecdsa" 18 "encoding/hex" 19 "encoding/json" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "strings" 26 "time" 27 28 "github.com/ethereum/go-ethereum/accounts" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/crypto" 31 "github.com/pborman/uuid" 32 ) 33 34 const ( 35 version = 3 36 ) 37 38 type Key struct { 39 Id uuid.UUID //唯一ID的版本4“随机”不是从密钥数据派生的 40 //为了简化查找,我们还存储了地址 41 Address common.Address 42 //我们只存储privkey,因为pubkey/地址可以从中派生 43 //此结构中的privkey始终为纯文本 44 PrivateKey *ecdsa.PrivateKey 45 } 46 47 type keyStore interface { 48 //从磁盘加载和解密密钥。 49 GetKey(addr common.Address, filename string, auth string) (*Key, error) 50 //写入并加密密钥。 51 StoreKey(filename string, k *Key, auth string) error 52 //将文件名与密钥目录联接,除非它已经是绝对目录。 53 JoinPath(filename string) string 54 } 55 56 type plainKeyJSON struct { 57 Address string `json:"address"` 58 PrivateKey string `json:"privatekey"` 59 Id string `json:"id"` 60 Version int `json:"version"` 61 } 62 63 type encryptedKeyJSONV3 struct { 64 Address string `json:"address"` 65 Crypto CryptoJSON `json:"crypto"` 66 Id string `json:"id"` 67 Version int `json:"version"` 68 } 69 70 type encryptedKeyJSONV1 struct { 71 Address string `json:"address"` 72 Crypto CryptoJSON `json:"crypto"` 73 Id string `json:"id"` 74 Version string `json:"version"` 75 } 76 77 type CryptoJSON struct { 78 Cipher string `json:"cipher"` 79 CipherText string `json:"ciphertext"` 80 CipherParams cipherparamsJSON `json:"cipherparams"` 81 KDF string `json:"kdf"` 82 KDFParams map[string]interface{} `json:"kdfparams"` 83 MAC string `json:"mac"` 84 } 85 86 type cipherparamsJSON struct { 87 IV string `json:"iv"` 88 } 89 90 func (k *Key) MarshalJSON() (j []byte, err error) { 91 jStruct := plainKeyJSON{ 92 hex.EncodeToString(k.Address[:]), 93 hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), 94 k.Id.String(), 95 version, 96 } 97 j, err = json.Marshal(jStruct) 98 return j, err 99 } 100 101 func (k *Key) UnmarshalJSON(j []byte) (err error) { 102 keyJSON := new(plainKeyJSON) 103 err = json.Unmarshal(j, &keyJSON) 104 if err != nil { 105 return err 106 } 107 108 u := new(uuid.UUID) 109 *u = uuid.Parse(keyJSON.Id) 110 k.Id = *u 111 addr, err := hex.DecodeString(keyJSON.Address) 112 if err != nil { 113 return err 114 } 115 privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey) 116 if err != nil { 117 return err 118 } 119 120 k.Address = common.BytesToAddress(addr) 121 k.PrivateKey = privkey 122 123 return nil 124 } 125 126 func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { 127 id := uuid.NewRandom() 128 key := &Key{ 129 Id: id, 130 Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), 131 PrivateKey: privateKeyECDSA, 132 } 133 return key 134 } 135 136 //newkeywordirecticap生成一个地址小于155位的密钥,以便它可以 137 //为了简化和更容易与其他libs兼容,我们在直接的icap规范中 138 //重试,直到第一个字节为0。 139 func NewKeyForDirectICAP(rand io.Reader) *Key { 140 randBytes := make([]byte, 64) 141 _, err := rand.Read(randBytes) 142 if err != nil { 143 panic("key generation: could not read from random source: " + err.Error()) 144 } 145 reader := bytes.NewReader(randBytes) 146 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) 147 if err != nil { 148 panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) 149 } 150 key := newKeyFromECDSA(privateKeyECDSA) 151 if !strings.HasPrefix(key.Address.Hex(), "0x00") { 152 return NewKeyForDirectICAP(rand) 153 } 154 return key 155 } 156 157 func newKey(rand io.Reader) (*Key, error) { 158 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) 159 if err != nil { 160 return nil, err 161 } 162 return newKeyFromECDSA(privateKeyECDSA), nil 163 } 164 165 func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) { 166 key, err := newKey(rand) 167 if err != nil { 168 return nil, accounts.Account{}, err 169 } 170 a := accounts.Account{ 171 Address: key.Address, 172 URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}, 173 } 174 if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { 175 zeroKey(key.PrivateKey) 176 return nil, a, err 177 } 178 return key, a, err 179 } 180 181 func writeTemporaryKeyFile(file string, content []byte) (string, error) { 182 //使用适当的权限创建keystore目录 183 //以防它还没有出现。 184 const dirPerm = 0700 185 if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { 186 return "", err 187 } 188 //原子写入:首先创建临时隐藏文件 189 //然后移动到位。tempfile指定模式0600。 190 f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") 191 if err != nil { 192 return "", err 193 } 194 if _, err := f.Write(content); err != nil { 195 f.Close() 196 os.Remove(f.Name()) 197 return "", err 198 } 199 f.Close() 200 return f.Name(), nil 201 } 202 203 func writeKeyFile(file string, content []byte) error { 204 name, err := writeTemporaryKeyFile(file, content) 205 if err != nil { 206 return err 207 } 208 return os.Rename(name, file) 209 } 210 211 //keyfilename实现了keyfiles的命名约定: 212 //UTC——<created_at UTC iso8601>-<address hex> 213 func keyFileName(keyAddr common.Address) string { 214 ts := time.Now().UTC() 215 return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:])) 216 } 217 218 func toISO8601(t time.Time) string { 219 var tz string 220 name, offset := t.Zone() 221 if name == "UTC" { 222 tz = "Z" 223 } else { 224 tz = fmt.Sprintf("%03d00", offset/3600) 225 } 226 return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", 227 t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) 228 } 229