github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/accounts/keystore/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 12:09:26</date>
    10  //</624342585237835776>
    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  	"path/filepath"
    36  
    37  	"github.com/ethereum/go-ethereum/common"
    38  	"github.com/ethereum/go-ethereum/common/math"
    39  	"github.com/ethereum/go-ethereum/crypto"
    40  	"github.com/pborman/uuid"
    41  	"golang.org/x/crypto/pbkdf2"
    42  	"golang.org/x/crypto/scrypt"
    43  )
    44  
    45  const (
    46  	keyHeaderKDF = "scrypt"
    47  
    48  //标准加密是加密算法的n个参数,使用256MB
    49  //在现代处理器上占用大约1秒的CPU时间。
    50  	StandardScryptN = 1 << 18
    51  
    52  //StandardScryptP是加密算法的P参数,使用256MB
    53  //在现代处理器上占用大约1秒的CPU时间。
    54  	StandardScryptP = 1
    55  
    56  //lightscryptn是加密算法的n个参数,使用4MB
    57  //在现代处理器上占用大约100毫秒的CPU时间。
    58  	LightScryptN = 1 << 12
    59  
    60  //lightscryptp是加密算法的p参数,使用4MB
    61  //在现代处理器上占用大约100毫秒的CPU时间。
    62  	LightScryptP = 6
    63  
    64  	scryptR     = 8
    65  	scryptDKLen = 32
    66  )
    67  
    68  type keyStorePassphrase struct {
    69  	keysDirPath string
    70  	scryptN     int
    71  	scryptP     int
    72  }
    73  
    74  func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
    75  //从密钥库加载密钥并解密其内容
    76  	keyjson, err := ioutil.ReadFile(filename)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	key, err := DecryptKey(keyjson, auth)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  //确保我们确实在使用请求的密钥(没有交换攻击)
    85  	if key.Address != addr {
    86  		return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
    87  	}
    88  	return key, nil
    89  }
    90  
    91  //storekey生成一个密钥,用auth加密并存储在给定的目录中
    92  func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
    93  	_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, rand.Reader, auth)
    94  	return a.Address, err
    95  }
    96  
    97  func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
    98  	keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	return writeKeyFile(filename, keyjson)
   103  }
   104  
   105  func (ks keyStorePassphrase) JoinPath(filename string) string {
   106  	if filepath.IsAbs(filename) {
   107  		return filename
   108  	}
   109  	return filepath.Join(ks.keysDirPath, filename)
   110  }
   111  
   112  //encryptkey使用指定的scrypt参数将密钥加密到JSON中
   113  //稍后可以解密的blob。
   114  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   115  	authArray := []byte(auth)
   116  
   117  	salt := make([]byte, 32)
   118  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   119  		panic("reading from crypto/rand failed: " + err.Error())
   120  	}
   121  	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	encryptKey := derivedKey[:16]
   126  	keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
   127  
   128  iv := make([]byte, aes.BlockSize) //十六
   129  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   130  		panic("reading from crypto/rand failed: " + err.Error())
   131  	}
   132  	cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   137  
   138  	scryptParamsJSON := make(map[string]interface{}, 5)
   139  	scryptParamsJSON["n"] = scryptN
   140  	scryptParamsJSON["r"] = scryptR
   141  	scryptParamsJSON["p"] = scryptP
   142  	scryptParamsJSON["dklen"] = scryptDKLen
   143  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   144  
   145  	cipherParamsJSON := cipherparamsJSON{
   146  		IV: hex.EncodeToString(iv),
   147  	}
   148  
   149  	cryptoStruct := cryptoJSON{
   150  		Cipher:       "aes-128-ctr",
   151  		CipherText:   hex.EncodeToString(cipherText),
   152  		CipherParams: cipherParamsJSON,
   153  		KDF:          keyHeaderKDF,
   154  		KDFParams:    scryptParamsJSON,
   155  		MAC:          hex.EncodeToString(mac),
   156  	}
   157  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   158  		hex.EncodeToString(key.Address[:]),
   159  		cryptoStruct,
   160  		key.Id.String(),
   161  		version,
   162  	}
   163  	return json.Marshal(encryptedKeyJSONV3)
   164  }
   165  
   166  //decryptkey从JSON blob中解密密钥,返回私钥本身。
   167  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   168  //将JSON解析为一个简单的映射以获取密钥版本
   169  	m := make(map[string]interface{})
   170  	if err := json.Unmarshal(keyjson, &m); err != nil {
   171  		return nil, err
   172  	}
   173  //根据版本,尝试以某种方式分析
   174  	var (
   175  		keyBytes, keyId []byte
   176  		err             error
   177  	)
   178  	if version, ok := m["version"].(string); ok && version == "1" {
   179  		k := new(encryptedKeyJSONV1)
   180  		if err := json.Unmarshal(keyjson, k); err != nil {
   181  			return nil, err
   182  		}
   183  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   184  	} else {
   185  		k := new(encryptedKeyJSONV3)
   186  		if err := json.Unmarshal(keyjson, k); err != nil {
   187  			return nil, err
   188  		}
   189  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   190  	}
   191  //处理任何解密错误并返回密钥
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	key := crypto.ToECDSAUnsafe(keyBytes)
   196  
   197  	return &Key{
   198  		Id:         uuid.UUID(keyId),
   199  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   200  		PrivateKey: key,
   201  	}, nil
   202  }
   203  
   204  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   205  	if keyProtected.Version != version {
   206  		return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   207  	}
   208  
   209  	if keyProtected.Crypto.Cipher != "aes-128-ctr" {
   210  		return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
   211  	}
   212  
   213  	keyId = uuid.Parse(keyProtected.Id)
   214  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   215  	if err != nil {
   216  		return nil, nil, err
   217  	}
   218  
   219  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   220  	if err != nil {
   221  		return nil, nil, err
   222  	}
   223  
   224  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   225  	if err != nil {
   226  		return nil, nil, err
   227  	}
   228  
   229  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   230  	if err != nil {
   231  		return nil, nil, err
   232  	}
   233  
   234  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   235  	if !bytes.Equal(calculatedMAC, mac) {
   236  		return nil, nil, ErrDecrypt
   237  	}
   238  
   239  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   240  	if err != nil {
   241  		return nil, nil, err
   242  	}
   243  	return plainText, keyId, err
   244  }
   245  
   246  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   247  	keyId = uuid.Parse(keyProtected.Id)
   248  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   249  	if err != nil {
   250  		return nil, nil, err
   251  	}
   252  
   253  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   254  	if err != nil {
   255  		return nil, nil, err
   256  	}
   257  
   258  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   259  	if err != nil {
   260  		return nil, nil, err
   261  	}
   262  
   263  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   264  	if err != nil {
   265  		return nil, nil, err
   266  	}
   267  
   268  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   269  	if !bytes.Equal(calculatedMAC, mac) {
   270  		return nil, nil, ErrDecrypt
   271  	}
   272  
   273  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   274  	if err != nil {
   275  		return nil, nil, err
   276  	}
   277  	return plainText, keyId, err
   278  }
   279  
   280  func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
   281  	authArray := []byte(auth)
   282  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   287  
   288  	if cryptoJSON.KDF == keyHeaderKDF {
   289  		n := ensureInt(cryptoJSON.KDFParams["n"])
   290  		r := ensureInt(cryptoJSON.KDFParams["r"])
   291  		p := ensureInt(cryptoJSON.KDFParams["p"])
   292  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   293  
   294  	} else if cryptoJSON.KDF == "pbkdf2" {
   295  		c := ensureInt(cryptoJSON.KDFParams["c"])
   296  		prf := cryptoJSON.KDFParams["prf"].(string)
   297  		if prf != "hmac-sha256" {
   298  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   299  		}
   300  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   301  		return key, nil
   302  	}
   303  
   304  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   305  }
   306  
   307  //TODO:在解组动态JSON时,我们可以不这样做吗?
   308  //为什么kdf参数中的整数以float64结尾,而不是后面的int
   309  //元帅?
   310  func ensureInt(x interface{}) int {
   311  	res, ok := x.(int)
   312  	if !ok {
   313  		res = int(x.(float64))
   314  	}
   315  	return res
   316  }
   317