github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/accounts/keystore/passphrase.go (about)

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