github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/accounts/keystore/passphrase.go (about)

     1  // Copyright 2014 The go-simplechain Authors
     2  // This file is part of the go-simplechain library.
     3  //
     4  // The go-simplechain library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-simplechain library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-simplechain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  /*
    18  
    19  This key store behaves as KeyStorePlain with the difference that
    20  the private key is encrypted and on disk uses another JSON encoding.
    21  
    22  The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
    23  
    24  */
    25  
    26  package keystore
    27  
    28  import (
    29  	"bytes"
    30  	"crypto/aes"
    31  	"crypto/rand"
    32  	"crypto/sha256"
    33  	"encoding/hex"
    34  	"encoding/json"
    35  	"fmt"
    36  	"io"
    37  	"io/ioutil"
    38  	"os"
    39  	"path/filepath"
    40  
    41  	"github.com/bigzoro/my_simplechain/accounts"
    42  	"github.com/bigzoro/my_simplechain/common"
    43  	"github.com/bigzoro/my_simplechain/common/math"
    44  	"github.com/bigzoro/my_simplechain/crypto"
    45  	"github.com/pborman/uuid"
    46  	"golang.org/x/crypto/pbkdf2"
    47  	"golang.org/x/crypto/scrypt"
    48  )
    49  
    50  const (
    51  	keyHeaderKDF = "scrypt"
    52  
    53  	// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
    54  	// memory and taking approximately 1s CPU time on a modern processor.
    55  	StandardScryptN = 1 << 18
    56  
    57  	// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
    58  	// memory and taking approximately 1s CPU time on a modern processor.
    59  	StandardScryptP = 1
    60  
    61  	// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
    62  	// memory and taking approximately 100ms CPU time on a modern processor.
    63  	LightScryptN = 1 << 12
    64  
    65  	// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
    66  	// memory and taking approximately 100ms CPU time on a modern processor.
    67  	LightScryptP = 6
    68  
    69  	scryptR     = 8
    70  	scryptDKLen = 32
    71  )
    72  
    73  type keyStorePassphrase struct {
    74  	keysDirPath string
    75  	scryptN     int
    76  	scryptP     int
    77  	// skipKeyFileVerification disables the security-feature which does
    78  	// reads and decrypts any newly created keyfiles. This should be 'false' in all
    79  	// cases except tests -- setting this to 'true' is not recommended.
    80  	skipKeyFileVerification bool
    81  }
    82  
    83  func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
    84  	// Load the key from the keystore and decrypt its contents
    85  	keyjson, err := ioutil.ReadFile(filename)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	key, err := DecryptKey(keyjson, auth)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	// Make sure we're really operating on the requested key (no swap attacks)
    94  	if key.Address != addr {
    95  		return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
    96  	}
    97  	return key, nil
    98  }
    99  
   100  // StoreKey generates a key, encrypts with 'auth' and stores in the given directory
   101  func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
   102  	_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
   103  	return a, err
   104  }
   105  
   106  func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
   107  	keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	// Write into temporary file
   112  	tmpName, err := writeTemporaryKeyFile(filename, keyjson)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	if !ks.skipKeyFileVerification {
   117  		// Verify that we can decrypt the file with the given password.
   118  		_, err = ks.GetKey(key.Address, tmpName, auth)
   119  		if err != nil {
   120  			msg := "An error was encountered when saving and verifying the keystore file. \n" +
   121  				"This indicates that the keystore is corrupted. \n" +
   122  				"The corrupted file is stored at \n%v\n" +
   123  				"Please file a ticket at:\n\n" +
   124  				"https://github.com/simplechain-org/go-simplechain/issues." +
   125  				"The error was : %s"
   126  			//lint:ignore ST1005 This is a message for the user
   127  			return fmt.Errorf(msg, tmpName, err)
   128  		}
   129  	}
   130  	return os.Rename(tmpName, filename)
   131  }
   132  
   133  func (ks keyStorePassphrase) StoreKeyByCA(key *Key, auth string) (*Key, accounts.Account, error) {
   134  	var a accounts.Account
   135  	keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
   136  	if err != nil {
   137  		return nil, a, err
   138  	}
   139  	key, err = DecryptKey(keyjson, auth)
   140  	if err != nil {
   141  		return nil, a, err
   142  	}
   143  	a = accounts.Account{
   144  		Address: key.Address,
   145  		URL:     accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
   146  	}
   147  	filename := a.URL.Path
   148  	// Write into temporary file,use simplechain publickey to generate
   149  	keyjson, err = EncryptKey(key, auth, ks.scryptN, ks.scryptP)
   150  	if err != nil {
   151  		return nil, a, err
   152  	}
   153  	tmpName, err := writeTemporaryKeyFile(filename, keyjson)
   154  	if err != nil {
   155  		return nil, a, err
   156  	}
   157  	return key, a, os.Rename(tmpName, filename)
   158  }
   159  
   160  func (ks keyStorePassphrase) JoinPath(filename string) string {
   161  	if filepath.IsAbs(filename) {
   162  		return filename
   163  	}
   164  	return filepath.Join(ks.keysDirPath, filename)
   165  }
   166  
   167  func (ks keyStorePassphrase) ReadCert(filename string) ([]byte, error) {
   168  	filePath := filepath.Join(ks.keysDirPath, filename)
   169  	fileBytes, err := ioutil.ReadFile(filePath)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	return fileBytes, nil
   174  }
   175  
   176  // Encryptdata encrypts the data given as 'data' with the password 'auth'.
   177  func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
   178  
   179  	salt := make([]byte, 32)
   180  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   181  		panic("reading from crypto/rand failed: " + err.Error())
   182  	}
   183  	derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
   184  	if err != nil {
   185  		return CryptoJSON{}, err
   186  	}
   187  	encryptKey := derivedKey[:16]
   188  
   189  	iv := make([]byte, aes.BlockSize) // 16
   190  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   191  		panic("reading from crypto/rand failed: " + err.Error())
   192  	}
   193  	cipherText, err := aesCTRXOR(encryptKey, data, iv)
   194  	if err != nil {
   195  		return CryptoJSON{}, err
   196  	}
   197  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   198  
   199  	scryptParamsJSON := make(map[string]interface{}, 5)
   200  	scryptParamsJSON["n"] = scryptN
   201  	scryptParamsJSON["r"] = scryptR
   202  	scryptParamsJSON["p"] = scryptP
   203  	scryptParamsJSON["dklen"] = scryptDKLen
   204  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   205  	cipherParamsJSON := cipherparamsJSON{
   206  		IV: hex.EncodeToString(iv),
   207  	}
   208  
   209  	cryptoStruct := CryptoJSON{
   210  		Cipher:       "aes-128-ctr",
   211  		CipherText:   hex.EncodeToString(cipherText),
   212  		CipherParams: cipherParamsJSON,
   213  		KDF:          keyHeaderKDF,
   214  		KDFParams:    scryptParamsJSON,
   215  		MAC:          hex.EncodeToString(mac),
   216  	}
   217  	return cryptoStruct, nil
   218  }
   219  
   220  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   221  // blob that can be decrypted later on.
   222  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   223  	keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
   224  	cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   229  		hex.EncodeToString(key.Address[:]),
   230  		cryptoStruct,
   231  		key.Id.String(),
   232  		version,
   233  	}
   234  	return json.Marshal(encryptedKeyJSONV3)
   235  }
   236  
   237  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   238  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   239  	// Parse the json into a simple map to fetch the key version
   240  	m := make(map[string]interface{})
   241  	if err := json.Unmarshal(keyjson, &m); err != nil {
   242  		return nil, err
   243  	}
   244  	// Depending on the version try to parse one way or another
   245  	var (
   246  		keyBytes, keyId []byte
   247  		err             error
   248  	)
   249  	if version, ok := m["version"].(string); ok && version == "1" {
   250  		k := new(encryptedKeyJSONV1)
   251  		if err := json.Unmarshal(keyjson, k); err != nil {
   252  			return nil, err
   253  		}
   254  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   255  	} else {
   256  		k := new(encryptedKeyJSONV3)
   257  		if err := json.Unmarshal(keyjson, k); err != nil {
   258  			return nil, err
   259  		}
   260  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   261  	}
   262  	// Handle any decryption errors and return the key
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	key := crypto.ToECDSAUnsafe(keyBytes)
   267  
   268  	return &Key{
   269  		Id:         uuid.UUID(keyId),
   270  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   271  		PrivateKey: key,
   272  	}, nil
   273  }
   274  
   275  func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
   276  	if cryptoJson.Cipher != "aes-128-ctr" {
   277  		return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
   278  	}
   279  	mac, err := hex.DecodeString(cryptoJson.MAC)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  
   284  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  
   289  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  
   294  	derivedKey, err := getKDFKey(cryptoJson, auth)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  
   299  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   300  	if !bytes.Equal(calculatedMAC, mac) {
   301  		return nil, ErrDecrypt
   302  	}
   303  
   304  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	return plainText, err
   309  }
   310  
   311  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   312  	if keyProtected.Version != version {
   313  		return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
   314  	}
   315  	keyId = uuid.Parse(keyProtected.Id)
   316  	plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
   317  	if err != nil {
   318  		return nil, nil, err
   319  	}
   320  	return plainText, keyId, err
   321  }
   322  
   323  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   324  	keyId = uuid.Parse(keyProtected.Id)
   325  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   326  	if err != nil {
   327  		return nil, nil, err
   328  	}
   329  
   330  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   331  	if err != nil {
   332  		return nil, nil, err
   333  	}
   334  
   335  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   336  	if err != nil {
   337  		return nil, nil, err
   338  	}
   339  
   340  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   341  	if err != nil {
   342  		return nil, nil, err
   343  	}
   344  
   345  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   346  	if !bytes.Equal(calculatedMAC, mac) {
   347  		return nil, nil, ErrDecrypt
   348  	}
   349  
   350  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   351  	if err != nil {
   352  		return nil, nil, err
   353  	}
   354  	return plainText, keyId, err
   355  }
   356  
   357  func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
   358  	authArray := []byte(auth)
   359  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   364  
   365  	if cryptoJSON.KDF == keyHeaderKDF {
   366  		n := ensureInt(cryptoJSON.KDFParams["n"])
   367  		r := ensureInt(cryptoJSON.KDFParams["r"])
   368  		p := ensureInt(cryptoJSON.KDFParams["p"])
   369  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   370  
   371  	} else if cryptoJSON.KDF == "pbkdf2" {
   372  		c := ensureInt(cryptoJSON.KDFParams["c"])
   373  		prf := cryptoJSON.KDFParams["prf"].(string)
   374  		if prf != "hmac-sha256" {
   375  			return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
   376  		}
   377  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   378  		return key, nil
   379  	}
   380  
   381  	return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
   382  }
   383  
   384  // TODO: can we do without this when unmarshalling dynamic JSON?
   385  // why do integers in KDF params end up as float64 and not int after
   386  // unmarshal?
   387  func ensureInt(x interface{}) int {
   388  	res, ok := x.(int)
   389  	if !ok {
   390  		res = int(x.(float64))
   391  	}
   392  	return res
   393  }