github.com/klaytn/klaytn@v1.12.1/accounts/keystore/key.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2014 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from accounts/keystore/key.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package keystore
    22  
    23  import (
    24  	"bytes"
    25  	"crypto/ecdsa"
    26  	"encoding/hex"
    27  	"encoding/json"
    28  	"fmt"
    29  	"io"
    30  	"os"
    31  	"path/filepath"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/klaytn/klaytn/accounts"
    36  	"github.com/klaytn/klaytn/common"
    37  	"github.com/klaytn/klaytn/crypto"
    38  	"github.com/pborman/uuid"
    39  )
    40  
    41  // Key represents a keystore storing private keys of an account.
    42  type Key interface {
    43  	json.Marshaler
    44  	json.Unmarshaler
    45  
    46  	// Returns key ID.
    47  	GetId() uuid.UUID
    48  
    49  	// Returns the address of the keystore.
    50  	GetAddress() common.Address
    51  
    52  	// Returns the default key of the keystore.
    53  	GetPrivateKey() *ecdsa.PrivateKey
    54  
    55  	// Returns all keys in the keystore.
    56  	GetPrivateKeys() [][]*ecdsa.PrivateKey
    57  
    58  	// Returns all keys in the specified role in the keystore.
    59  	GetPrivateKeysWithRole(role int) []*ecdsa.PrivateKey
    60  
    61  	// Resets all the keys in the keystore.
    62  	ResetPrivateKey()
    63  }
    64  
    65  type KeyV3 struct {
    66  	Id uuid.UUID // Version 4 "random" for unique id not derived from key data
    67  	// to simplify lookups we also store the address
    68  	Address common.Address
    69  	// we only store privkey as pubkey/address can be derived from it
    70  	// privkey in this struct is always in plaintext
    71  	PrivateKey *ecdsa.PrivateKey
    72  }
    73  
    74  type keyStore interface {
    75  	// Loads and decrypts the key from disk.
    76  	GetKey(addr common.Address, filename string, auth string) (Key, error)
    77  	// Writes and encrypts the key.
    78  	StoreKey(filename string, k Key, auth string) error
    79  	// Joins filename with the key directory unless it is already absolute.
    80  	JoinPath(filename string) string
    81  }
    82  
    83  type plainKeyJSON struct {
    84  	Address    string `json:"address"`
    85  	PrivateKey string `json:"privatekey"`
    86  	Id         string `json:"id"`
    87  	Version    int    `json:"version"`
    88  }
    89  
    90  type encryptedKeyJSONV3 struct {
    91  	Address string     `json:"address"`
    92  	Crypto  cryptoJSON `json:"crypto"`
    93  	Id      string     `json:"id"`
    94  	Version int        `json:"version"`
    95  }
    96  
    97  type encryptedKeyJSONV1 struct {
    98  	Address string     `json:"address"`
    99  	Crypto  cryptoJSON `json:"crypto"`
   100  	Id      string     `json:"id"`
   101  	Version string     `json:"version"`
   102  }
   103  
   104  type cryptoJSON struct {
   105  	Cipher       string                 `json:"cipher"`
   106  	CipherText   string                 `json:"ciphertext"`
   107  	CipherParams cipherparamsJSON       `json:"cipherparams"`
   108  	KDF          string                 `json:"kdf"`
   109  	KDFParams    map[string]interface{} `json:"kdfparams"`
   110  	MAC          string                 `json:"mac"`
   111  }
   112  
   113  type cipherparamsJSON struct {
   114  	IV string `json:"iv"`
   115  }
   116  
   117  func (k *KeyV3) MarshalJSON() (j []byte, err error) {
   118  	jStruct := plainKeyJSON{
   119  		hex.EncodeToString(k.Address[:]),
   120  		hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
   121  		k.Id.String(),
   122  		3,
   123  	}
   124  	j, err = json.Marshal(jStruct)
   125  	return j, err
   126  }
   127  
   128  func (k *KeyV3) UnmarshalJSON(j []byte) (err error) {
   129  	keyJSON := new(plainKeyJSON)
   130  	err = json.Unmarshal(j, &keyJSON)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	u := new(uuid.UUID)
   136  	*u = uuid.Parse(keyJSON.Id)
   137  	k.Id = *u
   138  	addr, err := hex.DecodeString(keyJSON.Address)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	k.Address = common.BytesToAddress(addr)
   148  	k.PrivateKey = privkey
   149  
   150  	return nil
   151  }
   152  
   153  func (k *KeyV3) GetId() uuid.UUID {
   154  	return k.Id
   155  }
   156  
   157  func (k *KeyV3) GetAddress() common.Address {
   158  	return k.Address
   159  }
   160  
   161  func (k *KeyV3) GetPrivateKey() *ecdsa.PrivateKey {
   162  	return k.PrivateKey
   163  }
   164  
   165  func (k *KeyV3) GetPrivateKeys() [][]*ecdsa.PrivateKey {
   166  	return [][]*ecdsa.PrivateKey{{k.PrivateKey}}
   167  }
   168  
   169  func (k *KeyV3) GetPrivateKeysWithRole(role int) []*ecdsa.PrivateKey {
   170  	return []*ecdsa.PrivateKey{k.PrivateKey}
   171  }
   172  
   173  func (k *KeyV3) ResetPrivateKey() {
   174  	zeroKey(k.PrivateKey)
   175  }
   176  
   177  func newKeyFromECDSAWithAddress(privateKeyECDSA *ecdsa.PrivateKey, address common.Address) Key {
   178  	id := uuid.NewRandom()
   179  	key := &KeyV4{
   180  		Id:          id,
   181  		Address:     address,
   182  		PrivateKeys: [][]*ecdsa.PrivateKey{{privateKeyECDSA}},
   183  	}
   184  	return key
   185  }
   186  
   187  func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) Key {
   188  	id := uuid.NewRandom()
   189  	key := &KeyV4{
   190  		Id:          id,
   191  		Address:     crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
   192  		PrivateKeys: [][]*ecdsa.PrivateKey{{privateKeyECDSA}},
   193  	}
   194  	return key
   195  }
   196  
   197  // NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
   198  // into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
   199  // retry until the first byte is 0.
   200  func NewKeyForDirectICAP(rand io.Reader) Key {
   201  	randBytes := make([]byte, 64)
   202  	_, err := rand.Read(randBytes)
   203  	if err != nil {
   204  		panic("key generation: could not read from random source: " + err.Error())
   205  	}
   206  	reader := bytes.NewReader(randBytes)
   207  	privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
   208  	if err != nil {
   209  		panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
   210  	}
   211  	key := newKeyFromECDSA(privateKeyECDSA)
   212  	if !strings.HasPrefix(key.GetAddress().Hex(), "0x00") {
   213  		return NewKeyForDirectICAP(rand)
   214  	}
   215  	return key
   216  }
   217  
   218  func newKey(rand io.Reader) (Key, error) {
   219  	privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	return newKeyFromECDSA(privateKeyECDSA), nil
   224  }
   225  
   226  func storeNewKey(ks keyStore, rand io.Reader, auth string) (Key, accounts.Account, error) {
   227  	key, err := newKey(rand)
   228  	if err != nil {
   229  		return nil, accounts.Account{}, err
   230  	}
   231  	a := accounts.Account{Address: key.GetAddress(), URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.GetAddress()))}}
   232  	if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
   233  		key.ResetPrivateKey()
   234  		return nil, a, err
   235  	}
   236  	return key, a, err
   237  }
   238  
   239  func writeTemporaryKeyFile(file string, content []byte) (string, error) {
   240  	// Create the keystore directory with appropriate permissions
   241  	// in case it is not present yet.
   242  	const dirPerm = 0o700
   243  	if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
   244  		return "", err
   245  	}
   246  	// Atomic write: create a temporary hidden file first
   247  	// then move it into place. TempFile assigns mode 0600.
   248  	f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
   249  	if err != nil {
   250  		return "", err
   251  	}
   252  	if _, err := f.Write(content); err != nil {
   253  		f.Close()
   254  		os.Remove(f.Name())
   255  		return "", err
   256  	}
   257  	f.Close()
   258  	return f.Name(), err
   259  }
   260  
   261  func writeKeyFile(file string, content []byte) error {
   262  	name, err := writeTemporaryKeyFile(file, content)
   263  	if err != nil {
   264  		return err
   265  	}
   266  	return os.Rename(name, file)
   267  }
   268  
   269  // keyFileName implements the naming convention for keyfiles:
   270  // UTC--<created_at UTC ISO8601>-<address hex>
   271  func keyFileName(keyAddr common.Address) string {
   272  	ts := time.Now().UTC()
   273  	return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:]))
   274  }
   275  
   276  func toISO8601(t time.Time) string {
   277  	var tz string
   278  	name, offset := t.Zone()
   279  	if name == "UTC" {
   280  		tz = "Z"
   281  	} else {
   282  		tz = fmt.Sprintf("%03d00", offset/3600)
   283  	}
   284  	return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
   285  }