github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/accounts/keystore/passphrase.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 //</624450064458190848> 11 12 13 /* 14 15 此密钥存储区的行为与密钥存储区的区别在于 16 私钥是加密的,在磁盘上使用另一个JSON编码。 17 18 密码记录在https://github.com/ethereum/wiki/wiki/web3-secret-storage-definition上。 19 20 **/ 21 22 23 package keystore 24 25 import ( 26 "bytes" 27 "crypto/aes" 28 "crypto/rand" 29 "crypto/sha256" 30 "encoding/hex" 31 "encoding/json" 32 "fmt" 33 "io" 34 "io/ioutil" 35 "os" 36 "path/filepath" 37 38 "github.com/ethereum/go-ethereum/common" 39 "github.com/ethereum/go-ethereum/common/math" 40 "github.com/ethereum/go-ethereum/crypto" 41 "github.com/pborman/uuid" 42 "golang.org/x/crypto/pbkdf2" 43 "golang.org/x/crypto/scrypt" 44 ) 45 46 const ( 47 keyHeaderKDF = "scrypt" 48 49 //标准加密是加密算法的n个参数,使用256MB 50 //在现代处理器上占用大约1秒的CPU时间。 51 StandardScryptN = 1 << 18 52 53 //StandardScryptP是加密算法的P参数,使用256MB 54 //在现代处理器上占用大约1秒的CPU时间。 55 StandardScryptP = 1 56 57 //lightscryptn是加密算法的n个参数,使用4MB 58 //在现代处理器上占用大约100毫秒的CPU时间。 59 LightScryptN = 1 << 12 60 61 //lightscryptp是加密算法的p参数,使用4MB 62 //在现代处理器上占用大约100毫秒的CPU时间。 63 LightScryptP = 6 64 65 scryptR = 8 66 scryptDKLen = 32 67 ) 68 69 type keyStorePassphrase struct { 70 keysDirPath string 71 scryptN int 72 scryptP int 73 //skipkeyfileverification禁用的安全功能 74 //读取和解密任何新创建的关键文件。这应该是“错误的” 75 //除了测试之外的情况——不建议将其设置为“true”。 76 skipKeyFileVerification bool 77 } 78 79 func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { 80 //从密钥库加载密钥并解密其内容 81 keyjson, err := ioutil.ReadFile(filename) 82 if err != nil { 83 return nil, err 84 } 85 key, err := DecryptKey(keyjson, auth) 86 if err != nil { 87 return nil, err 88 } 89 //确保我们确实在使用请求的密钥(没有交换攻击) 90 if key.Address != addr { 91 return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) 92 } 93 return key, nil 94 } 95 96 //storekey生成一个密钥,用auth加密并存储在给定的目录中 97 func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) { 98 _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth) 99 return a.Address, err 100 } 101 102 func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { 103 keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) 104 if err != nil { 105 return err 106 } 107 //写入临时文件 108 tmpName, err := writeTemporaryKeyFile(filename, keyjson) 109 if err != nil { 110 return err 111 } 112 if !ks.skipKeyFileVerification { 113 //验证我们是否可以用给定的密码解密文件。 114 _, err = ks.GetKey(key.Address, tmpName, auth) 115 if err != nil { 116 msg := "An error was encountered when saving and verifying the keystore file. \n" + 117 "This indicates that the keystore is corrupted. \n" + 118 "The corrupted file is stored at \n%v\n" + 119 "Please file a ticket at:\n\n" + 120 "https://github.com/ethereum/go-ethereum/issues.+ 121 "The error was : %s" 122 return fmt.Errorf(msg, tmpName, err) 123 } 124 } 125 return os.Rename(tmpName, filename) 126 } 127 128 func (ks keyStorePassphrase) JoinPath(filename string) string { 129 if filepath.IsAbs(filename) { 130 return filename 131 } 132 return filepath.Join(ks.keysDirPath, filename) 133 } 134 135 //encryptdata用密码'auth'加密作为'data'提供的数据。 136 func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { 137 138 salt := make([]byte, 32) 139 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 140 panic("reading from crypto/rand failed: " + err.Error()) 141 } 142 derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) 143 if err != nil { 144 return CryptoJSON{}, err 145 } 146 encryptKey := derivedKey[:16] 147 148 iv := make([]byte, aes.BlockSize) //十六 149 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 150 panic("reading from crypto/rand failed: " + err.Error()) 151 } 152 cipherText, err := aesCTRXOR(encryptKey, data, iv) 153 if err != nil { 154 return CryptoJSON{}, err 155 } 156 mac := crypto.Keccak256(derivedKey[16:32], cipherText) 157 158 scryptParamsJSON := make(map[string]interface{}, 5) 159 scryptParamsJSON["n"] = scryptN 160 scryptParamsJSON["r"] = scryptR 161 scryptParamsJSON["p"] = scryptP 162 scryptParamsJSON["dklen"] = scryptDKLen 163 scryptParamsJSON["salt"] = hex.EncodeToString(salt) 164 cipherParamsJSON := cipherparamsJSON{ 165 IV: hex.EncodeToString(iv), 166 } 167 168 cryptoStruct := CryptoJSON{ 169 Cipher: "aes-128-ctr", 170 CipherText: hex.EncodeToString(cipherText), 171 CipherParams: cipherParamsJSON, 172 KDF: keyHeaderKDF, 173 KDFParams: scryptParamsJSON, 174 MAC: hex.EncodeToString(mac), 175 } 176 return cryptoStruct, nil 177 } 178 179 //encryptkey使用指定的scrypt参数将密钥加密到JSON中 180 //稍后可以解密的blob。 181 func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { 182 keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) 183 cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) 184 if err != nil { 185 return nil, err 186 } 187 encryptedKeyJSONV3 := encryptedKeyJSONV3{ 188 hex.EncodeToString(key.Address[:]), 189 cryptoStruct, 190 key.Id.String(), 191 version, 192 } 193 return json.Marshal(encryptedKeyJSONV3) 194 } 195 196 //decryptkey从JSON blob中解密密钥,返回私钥本身。 197 func DecryptKey(keyjson []byte, auth string) (*Key, error) { 198 //将JSON解析为一个简单的映射以获取密钥版本 199 m := make(map[string]interface{}) 200 if err := json.Unmarshal(keyjson, &m); err != nil { 201 return nil, err 202 } 203 //根据版本,尝试以某种方式分析 204 var ( 205 keyBytes, keyId []byte 206 err error 207 ) 208 if version, ok := m["version"].(string); ok && version == "1" { 209 k := new(encryptedKeyJSONV1) 210 if err := json.Unmarshal(keyjson, k); err != nil { 211 return nil, err 212 } 213 keyBytes, keyId, err = decryptKeyV1(k, auth) 214 } else { 215 k := new(encryptedKeyJSONV3) 216 if err := json.Unmarshal(keyjson, k); err != nil { 217 return nil, err 218 } 219 keyBytes, keyId, err = decryptKeyV3(k, auth) 220 } 221 //处理任何解密错误并返回密钥 222 if err != nil { 223 return nil, err 224 } 225 key := crypto.ToECDSAUnsafe(keyBytes) 226 227 return &Key{ 228 Id: uuid.UUID(keyId), 229 Address: crypto.PubkeyToAddress(key.PublicKey), 230 PrivateKey: key, 231 }, nil 232 } 233 234 func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { 235 if cryptoJson.Cipher != "aes-128-ctr" { 236 return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher) 237 } 238 mac, err := hex.DecodeString(cryptoJson.MAC) 239 if err != nil { 240 return nil, err 241 } 242 243 iv, err := hex.DecodeString(cryptoJson.CipherParams.IV) 244 if err != nil { 245 return nil, err 246 } 247 248 cipherText, err := hex.DecodeString(cryptoJson.CipherText) 249 if err != nil { 250 return nil, err 251 } 252 253 derivedKey, err := getKDFKey(cryptoJson, auth) 254 if err != nil { 255 return nil, err 256 } 257 258 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 259 if !bytes.Equal(calculatedMAC, mac) { 260 return nil, ErrDecrypt 261 } 262 263 plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 264 if err != nil { 265 return nil, err 266 } 267 return plainText, err 268 } 269 270 func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { 271 if keyProtected.Version != version { 272 return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) 273 } 274 keyId = uuid.Parse(keyProtected.Id) 275 plainText, err := DecryptDataV3(keyProtected.Crypto, auth) 276 if err != nil { 277 return nil, nil, err 278 } 279 return plainText, keyId, err 280 } 281 282 func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { 283 keyId = uuid.Parse(keyProtected.Id) 284 mac, err := hex.DecodeString(keyProtected.Crypto.MAC) 285 if err != nil { 286 return nil, nil, err 287 } 288 289 iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) 290 if err != nil { 291 return nil, nil, err 292 } 293 294 cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) 295 if err != nil { 296 return nil, nil, err 297 } 298 299 derivedKey, err := getKDFKey(keyProtected.Crypto, auth) 300 if err != nil { 301 return nil, nil, err 302 } 303 304 calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) 305 if !bytes.Equal(calculatedMAC, mac) { 306 return nil, nil, ErrDecrypt 307 } 308 309 plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) 310 if err != nil { 311 return nil, nil, err 312 } 313 return plainText, keyId, err 314 } 315 316 func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { 317 authArray := []byte(auth) 318 salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) 319 if err != nil { 320 return nil, err 321 } 322 dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) 323 324 if cryptoJSON.KDF == keyHeaderKDF { 325 n := ensureInt(cryptoJSON.KDFParams["n"]) 326 r := ensureInt(cryptoJSON.KDFParams["r"]) 327 p := ensureInt(cryptoJSON.KDFParams["p"]) 328 return scrypt.Key(authArray, salt, n, r, p, dkLen) 329 330 } else if cryptoJSON.KDF == "pbkdf2" { 331 c := ensureInt(cryptoJSON.KDFParams["c"]) 332 prf := cryptoJSON.KDFParams["prf"].(string) 333 if prf != "hmac-sha256" { 334 return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) 335 } 336 key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) 337 return key, nil 338 } 339 340 return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) 341 } 342 343 //TODO:在解组动态JSON时,我们可以不这样做吗? 344 //为什么kdf参数中的整数以float64结尾,而不是后面的int 345 //元帅? 346 func ensureInt(x interface{}) int { 347 res, ok := x.(int) 348 if !ok { 349 res = int(x.(float64)) 350 } 351 return res 352 } 353