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