github.com/halybang/go-ethereum@v1.0.5-0.20180325041310-3b262bc1367c/accounts/keystore/key.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  package keystore
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"encoding/hex"
    23  	"encoding/json"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  	"time"
    31  
    32  	"errors"
    33  
    34  	"github.com/btcsuite/btcd/btcec"
    35  	"github.com/pborman/uuid"
    36  	"github.com/wanchain/go-wanchain/accounts"
    37  	"github.com/wanchain/go-wanchain/common"
    38  	"github.com/wanchain/go-wanchain/common/math"
    39  	"github.com/wanchain/go-wanchain/crypto"
    40  )
    41  
    42  const (
    43  	version = 3
    44  )
    45  
    46  type Key struct {
    47  	Id uuid.UUID // Version 4 "random" for unique id not derived from key data
    48  	// to simplify lookups we also store the address
    49  	Address common.Address
    50  	// we only store privkey as pubkey/address can be derived from it
    51  	// privkey in this struct is always in plaintext
    52  	PrivateKey *ecdsa.PrivateKey
    53  	// add a second privkey for privary
    54  	PrivateKey2 *ecdsa.PrivateKey
    55  	// compact wanchain address format
    56  	WAddress common.WAddress
    57  }
    58  
    59  // Used to import and export raw keypair
    60  type keyPair struct {
    61  	D  string `json:"privateKey"`
    62  	D1 string `json:"privateKey1"`
    63  }
    64  
    65  type keyStore interface {
    66  	// Loads and decrypts the key from disk
    67  	GetKey(addr common.Address, filename string, auth string) (*Key, error)
    68  	// Loads an encrypted keyfile from disk
    69  	GetEncryptedKey(addr common.Address, filename string) (*Key, error)
    70  	// Writes and encrypts the key
    71  	StoreKey(filename string, k *Key, auth string) error
    72  	// Joins filename with the key directory unless it is already absolute.
    73  	JoinPath(filename string) string
    74  }
    75  
    76  type plainKeyJSON struct {
    77  	Address    string `json:"address"`
    78  	PrivateKey string `json:"privatekey"`
    79  	Id         string `json:"id"`
    80  	Version    int    `json:"version"`
    81  }
    82  
    83  type encryptedKeyJSONV3 struct {
    84  	Address  string     `json:"address"`
    85  	Crypto   cryptoJSON `json:"crypto"`
    86  	Crypto2  cryptoJSON `json:"crypto2"`
    87  	Id       string     `json:"id"`
    88  	Version  int        `json:"version"`
    89  	WAddress string     `json:"waddress"`
    90  }
    91  
    92  type encryptedKeyJSONV1 struct {
    93  	Address string     `json:"address"`
    94  	Crypto  cryptoJSON `json:"crypto"`
    95  	Id      string     `json:"id"`
    96  	Version string     `json:"version"`
    97  }
    98  
    99  type cryptoJSON struct {
   100  	Cipher       string                 `json:"cipher"`
   101  	CipherText   string                 `json:"ciphertext"`
   102  	CipherParams cipherparamsJSON       `json:"cipherparams"`
   103  	KDF          string                 `json:"kdf"`
   104  	KDFParams    map[string]interface{} `json:"kdfparams"`
   105  	MAC          string                 `json:"mac"`
   106  }
   107  
   108  type cipherparamsJSON struct {
   109  	IV string `json:"iv"`
   110  }
   111  
   112  func (k *Key) MarshalJSON() (j []byte, err error) {
   113  	jStruct := plainKeyJSON{
   114  		k.Address.Hex()[2:],
   115  		hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
   116  		k.Id.String(),
   117  		version,
   118  	}
   119  	j, err = json.Marshal(jStruct)
   120  	return j, err
   121  }
   122  
   123  func (k *Key) UnmarshalJSON(j []byte) (err error) {
   124  	keyJSON := new(plainKeyJSON)
   125  	err = json.Unmarshal(j, &keyJSON)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	u := new(uuid.UUID)
   131  	*u = uuid.Parse(keyJSON.Id)
   132  	k.Id = *u
   133  	addr, err := hex.DecodeString(keyJSON.Address)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	k.Address = common.BytesToAddress(addr)
   143  	k.PrivateKey = privkey
   144  
   145  	return nil
   146  }
   147  
   148  func newKeyFromECDSA(sk1, sk2 *ecdsa.PrivateKey) *Key {
   149  	id := uuid.NewRandom()
   150  	key := &Key{
   151  		Id:          id,
   152  		Address:     crypto.PubkeyToAddress(sk1.PublicKey),
   153  		PrivateKey:  sk1,
   154  		PrivateKey2: sk2,
   155  	}
   156  
   157  	updateWaddress(key)
   158  	return key
   159  }
   160  
   161  // updateWaddress adds WAddress field to the Key struct
   162  func updateWaddress(k *Key) {
   163  	k.WAddress = *GenerateWaddressFromPK(&k.PrivateKey.PublicKey, &k.PrivateKey2.PublicKey)
   164  }
   165  
   166  // ECDSAPKCompression serializes a public key in a 33-byte compressed format from btcec
   167  func ECDSAPKCompression(p *ecdsa.PublicKey) []byte {
   168  	const pubkeyCompressed byte = 0x2
   169  	b := make([]byte, 0, 33)
   170  	format := pubkeyCompressed
   171  	if p.Y.Bit(0) == 1 {
   172  		format |= 0x1
   173  	}
   174  	b = append(b, format)
   175  	b = append(b, math.PaddedBigBytes(p.X, 32)...)
   176  	return b
   177  }
   178  
   179  // NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
   180  // into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
   181  // retry until the first byte is 0.
   182  func NewKeyForDirectICAP(rand io.Reader) *Key {
   183  	randBytes := make([]byte, 64*2)
   184  	_, err := rand.Read(randBytes)
   185  	if err != nil {
   186  		panic("key generation: could not read from random source: " + err.Error())
   187  	}
   188  	reader := bytes.NewReader(randBytes)
   189  	sk1, err := ecdsa.GenerateKey(crypto.S256(), reader)
   190  	if err != nil {
   191  		panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
   192  	}
   193  
   194  	sk2, err := ecdsa.GenerateKey(crypto.S256(), reader)
   195  	if err != nil {
   196  		panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
   197  	}
   198  	key := newKeyFromECDSA(sk1, sk2)
   199  	if !strings.HasPrefix(key.Address.Hex(), "0x00") {
   200  		return NewKeyForDirectICAP(rand)
   201  	}
   202  	return key
   203  }
   204  
   205  func newKey(rand io.Reader) (*Key, error) {
   206  	privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	privateKeyECDSA2, err := ecdsa.GenerateKey(crypto.S256(), rand)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	return newKeyFromECDSA(privateKeyECDSA, privateKeyECDSA2), nil
   216  }
   217  
   218  func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
   219  	key, err := newKey(rand)
   220  	if err != nil {
   221  		return nil, accounts.Account{}, err
   222  	}
   223  	a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}}
   224  	if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
   225  		zeroKey(key.PrivateKey)
   226  		return nil, a, err
   227  	}
   228  	return key, a, err
   229  }
   230  
   231  func writeKeyFile(file string, content []byte) error {
   232  	// Create the keystore directory with appropriate permissions
   233  	// in case it is not present yet.
   234  	const dirPerm = 0700
   235  	if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
   236  		return err
   237  	}
   238  	// Atomic write: create a temporary hidden file first
   239  	// then move it into place. TempFile assigns mode 0600.
   240  	f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
   241  	if err != nil {
   242  		return err
   243  	}
   244  	if _, err := f.Write(content); err != nil {
   245  		f.Close()
   246  		os.Remove(f.Name())
   247  		return err
   248  	}
   249  	f.Close()
   250  	return os.Rename(f.Name(), file)
   251  }
   252  
   253  // keyFileName implements the naming convention for keyfiles:
   254  // UTC--<created_at UTC ISO8601>-<address hex>
   255  func keyFileName(keyAddr common.Address) string {
   256  	ts := time.Now().UTC()
   257  	return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), keyAddr.Hex()[2:])
   258  }
   259  
   260  func toISO8601(t time.Time) string {
   261  	var tz string
   262  	name, offset := t.Zone()
   263  	if name == "UTC" {
   264  		tz = "Z"
   265  	} else {
   266  		tz = fmt.Sprintf("%03d00", offset/3600)
   267  	}
   268  	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)
   269  }
   270  
   271  // GeneratePKPairFromWAddress represents the keystore to retrieve public key-pair from given WAddress
   272  func GeneratePKPairFromWAddress(w []byte) (*ecdsa.PublicKey, *ecdsa.PublicKey, error) {
   273  	if len(w) != common.WAddressLength {
   274  		return nil, nil, ErrWAddressInvalid
   275  	}
   276  
   277  	tmp := make([]byte, 33)
   278  	copy(tmp[:], w[:33])
   279  	curve := btcec.S256()
   280  	PK1, err := btcec.ParsePubKey(tmp, curve)
   281  	if err != nil {
   282  		return nil, nil, err
   283  	}
   284  
   285  	copy(tmp[:], w[33:])
   286  	PK2, err := btcec.ParsePubKey(tmp, curve)
   287  	if err != nil {
   288  		return nil, nil, err
   289  	}
   290  
   291  	return (*ecdsa.PublicKey)(PK1), (*ecdsa.PublicKey)(PK2), nil
   292  }
   293  
   294  func GenerateWaddressFromPK(A *ecdsa.PublicKey, B *ecdsa.PublicKey) *common.WAddress {
   295  	var tmp common.WAddress
   296  	copy(tmp[:33], ECDSAPKCompression(A))
   297  	copy(tmp[33:], ECDSAPKCompression(B))
   298  	return &tmp
   299  }
   300  
   301  func WaddrFromUncompressedRawBytes(raw []byte) (*common.WAddress, error) {
   302  	if len(raw) != 32*2*2 {
   303  		return nil, errors.New("invalid uncompressed wan address len")
   304  	}
   305  
   306  	pub := make([]byte, 65)
   307  	pub[0] = 0x004
   308  	copy(pub[1:], raw[:64])
   309  	A := crypto.ToECDSAPub(pub)
   310  	copy(pub[1:], raw[64:])
   311  	B := crypto.ToECDSAPub(pub)
   312  	return GenerateWaddressFromPK(A, B), nil
   313  }
   314  
   315  func WaddrToUncompressedRawBytes(waddr []byte) ([]byte, error) {
   316  	if len(waddr) != common.WAddressLength {
   317  		return nil, ErrWAddressInvalid
   318  	}
   319  
   320  	A, B, err := GeneratePKPairFromWAddress(waddr)
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  
   325  	u := make([]byte, 32*2*2)
   326  	ax := math.PaddedBigBytes(A.X, 32)
   327  	ay := math.PaddedBigBytes(A.Y, 32)
   328  	bx := math.PaddedBigBytes(B.X, 32)
   329  	by := math.PaddedBigBytes(B.Y, 32)
   330  	copy(u[0:], ax[:32])
   331  	copy(u[32:], ay[:32])
   332  	copy(u[64:], bx[:32])
   333  	copy(u[96:], by[:32])
   334  
   335  	return u, nil
   336  }
   337  
   338  // LoadECDSAPair loads a secp256k1 private key pair from the given file
   339  func LoadECDSAPair(file string) (*ecdsa.PrivateKey, *ecdsa.PrivateKey, error) {
   340  	// read the given file including private key pair
   341  	kp := keyPair{}
   342  
   343  	raw, err := ioutil.ReadFile(file)
   344  	if err != nil {
   345  		return nil, nil, err
   346  	}
   347  
   348  	err = json.Unmarshal(raw, &kp)
   349  	if err != nil {
   350  		return nil, nil, err
   351  	}
   352  
   353  	// Decode the key pair
   354  	d, err := hex.DecodeString(kp.D)
   355  	if err != nil {
   356  		return nil, nil, err
   357  	}
   358  	d1, err := hex.DecodeString(kp.D1)
   359  	if err != nil {
   360  		return nil, nil, err
   361  	}
   362  
   363  	// Generate ecdsa private keys
   364  	sk, err := crypto.ToECDSA(d)
   365  	if err != nil {
   366  		return nil, nil, err
   367  	}
   368  
   369  	sk1, err := crypto.ToECDSA(d1)
   370  	if err != nil {
   371  		return nil, nil, err
   372  	}
   373  
   374  	return sk, sk1, err
   375  }
   376  
   377  // ExportECDSAPair returns an ecdsa-private-key pair
   378  // func ExportECDSAPair(d, d1, fp string) error {
   379  // 	kp := keyPair{
   380  // 		D:  d,
   381  // 		D1: d1,
   382  // 	}
   383  // 	log.Info("Exporting ECDSA Prikave-Key-Pair", "file", fp)
   384  // 	fh, err := os.OpenFile(fp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
   385  // 	if err != nil {
   386  // 		return err
   387  // 	}
   388  // 	defer fh.Close()
   389  
   390  // 	var fileWriter io.Writer = fh
   391  // 	err = json.NewEncoder(fileWriter).Encode(kp)
   392  // 	return err
   393  // }
   394  
   395  // func ExportECDSAPairStr(d, d1 string) (string, error) {
   396  // 	kp := keyPair{
   397  // 		D:  d,
   398  // 		D1: d1,
   399  // 	}
   400  // 	r, err := json.Marshal(kp)
   401  // 	if err != nil {
   402  // 		return "", err
   403  // 	}
   404  
   405  // 	return string(r), err
   406  // }