github.com/theQRL/go-zond@v0.2.1/accounts/keystore/passphrase.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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-ethereum 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-ethereum 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/cipher"
    32  	"crypto/rand"
    33  	"crypto/sha256"
    34  	"encoding/hex"
    35  	"encoding/json"
    36  	"fmt"
    37  	"io"
    38  	"os"
    39  	"path/filepath"
    40  
    41  	"github.com/google/uuid"
    42  	"github.com/theQRL/go-zond/accounts"
    43  	"github.com/theQRL/go-zond/common"
    44  	"github.com/theQRL/go-zond/crypto"
    45  	"github.com/theQRL/go-zond/crypto/pqcrypto"
    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 := os.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}, 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/theQRL/go-zond/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) JoinPath(filename string) string {
   134  	if filepath.IsAbs(filename) {
   135  		return filename
   136  	}
   137  	return filepath.Join(ks.keysDirPath, filename)
   138  }
   139  
   140  // Encryptdata encrypts the data given as 'data' with the password 'auth'.
   141  func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
   142  	salt := make([]byte, 32)
   143  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   144  		panic("reading from crypto/rand failed: " + err.Error())
   145  	}
   146  	derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
   147  	if err != nil {
   148  		return CryptoJSON{}, err
   149  	}
   150  	encryptKey := derivedKey[:16]
   151  
   152  	iv := make([]byte, aes.BlockSize) // 16
   153  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   154  		panic("reading from crypto/rand failed: " + err.Error())
   155  	}
   156  	cipherText, err := aesCTRXOR(encryptKey, data, iv)
   157  	if err != nil {
   158  		return CryptoJSON{}, err
   159  	}
   160  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   161  
   162  	scryptParamsJSON := make(map[string]interface{}, 5)
   163  	scryptParamsJSON["n"] = scryptN
   164  	scryptParamsJSON["r"] = scryptR
   165  	scryptParamsJSON["p"] = scryptP
   166  	scryptParamsJSON["dklen"] = scryptDKLen
   167  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   168  	cipherParamsJSON := cipherparamsJSON{
   169  		IV: hex.EncodeToString(iv),
   170  	}
   171  
   172  	cryptoStruct := CryptoJSON{
   173  		Cipher:       "aes-128-ctr",
   174  		CipherText:   hex.EncodeToString(cipherText),
   175  		CipherParams: cipherParamsJSON,
   176  		KDF:          keyHeaderKDF,
   177  		KDFParams:    scryptParamsJSON,
   178  		MAC:          hex.EncodeToString(mac),
   179  	}
   180  	return cryptoStruct, nil
   181  }
   182  
   183  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   184  // blob that can be decrypted later on.
   185  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   186  	seed := key.Dilithium.GetSeed()
   187  	cryptoStruct, err := EncryptDataV3(seed[:], []byte(auth), scryptN, scryptP)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   192  		fmt.Sprintf("%#x", key.Address),
   193  		cryptoStruct,
   194  		key.Id.String(),
   195  		version,
   196  	}
   197  	return json.Marshal(encryptedKeyJSONV3)
   198  }
   199  
   200  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   201  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   202  	// Parse the json into a simple map to fetch the key version
   203  	m := make(map[string]interface{})
   204  	if err := json.Unmarshal(keyjson, &m); err != nil {
   205  		return nil, err
   206  	}
   207  	// Depending on the version try to parse one way or another
   208  	var (
   209  		keyBytes, keyId []byte
   210  		err             error
   211  	)
   212  
   213  	k := new(encryptedKeyJSONV3)
   214  	if err := json.Unmarshal(keyjson, k); err != nil {
   215  		return nil, err
   216  	}
   217  	keyBytes, keyId, err = decryptKeyV3(k, auth)
   218  
   219  	// Handle any decryption errors and return the key
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	d := pqcrypto.ToDilithiumUnsafe(keyBytes)
   224  	id, err := uuid.FromBytes(keyId)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	return &Key{
   229  		Id:        id,
   230  		Address:   d.GetAddress(),
   231  		Dilithium: d,
   232  	}, nil
   233  }
   234  
   235  func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
   236  	if cryptoJson.Cipher != "aes-128-ctr" {
   237  		return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
   238  	}
   239  	mac, err := hex.DecodeString(cryptoJson.MAC)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  
   249  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	derivedKey, err := getKDFKey(cryptoJson, auth)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   260  	if !bytes.Equal(calculatedMAC, mac) {
   261  		return nil, ErrDecrypt
   262  	}
   263  
   264  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	return plainText, err
   269  }
   270  
   271  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   272  	if keyProtected.Version != version {
   273  		return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
   274  	}
   275  	keyUUID, err := uuid.Parse(keyProtected.Id)
   276  	if err != nil {
   277  		return nil, nil, err
   278  	}
   279  	keyId = keyUUID[:]
   280  	plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
   281  	if err != nil {
   282  		return nil, nil, err
   283  	}
   284  	return plainText, keyId, err
   285  }
   286  
   287  func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
   288  	authArray := []byte(auth)
   289  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   294  
   295  	if cryptoJSON.KDF == keyHeaderKDF {
   296  		n := ensureInt(cryptoJSON.KDFParams["n"])
   297  		r := ensureInt(cryptoJSON.KDFParams["r"])
   298  		p := ensureInt(cryptoJSON.KDFParams["p"])
   299  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   300  	} else if cryptoJSON.KDF == "pbkdf2" {
   301  		c := ensureInt(cryptoJSON.KDFParams["c"])
   302  		prf := cryptoJSON.KDFParams["prf"].(string)
   303  		if prf != "hmac-sha256" {
   304  			return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
   305  		}
   306  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   307  		return key, nil
   308  	}
   309  
   310  	return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
   311  }
   312  
   313  // TODO: can we do without this when unmarshalling dynamic JSON?
   314  // why do integers in KDF params end up as float64 and not int after
   315  // unmarshal?
   316  func ensureInt(x interface{}) int {
   317  	res, ok := x.(int)
   318  	if !ok {
   319  		res = int(x.(float64))
   320  	}
   321  	return res
   322  }
   323  
   324  func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
   325  	// AES-128 is selected due to size of encryptKey.
   326  	aesBlock, err := aes.NewCipher(key)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  	stream := cipher.NewCTR(aesBlock, iv)
   331  	outText := make([]byte, len(inText))
   332  	stream.XORKeyStream(outText, inText)
   333  	return outText, err
   334  }
   335  
   336  func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
   337  	aesBlock, err := aes.NewCipher(key)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
   342  	paddedPlaintext := make([]byte, len(cipherText))
   343  	decrypter.CryptBlocks(paddedPlaintext, cipherText)
   344  	plaintext := pkcs7Unpad(paddedPlaintext)
   345  	if plaintext == nil {
   346  		return nil, ErrDecrypt
   347  	}
   348  	return plaintext, err
   349  }
   350  
   351  // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
   352  func pkcs7Unpad(in []byte) []byte {
   353  	if len(in) == 0 {
   354  		return nil
   355  	}
   356  
   357  	padding := in[len(in)-1]
   358  	if int(padding) > len(in) || padding > aes.BlockSize {
   359  		return nil
   360  	} else if padding == 0 {
   361  		return nil
   362  	}
   363  
   364  	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
   365  		if in[i] != padding {
   366  			return nil
   367  		}
   368  	}
   369  	return in[:len(in)-int(padding)]
   370  }