github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/store/secret.go (about)

     1  /*
     2   * Copyright (c) 2018-2020 vChain, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package store
    10  
    11  import (
    12  	"crypto/ecdsa"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"path/filepath"
    17  	"strings"
    18  
    19  	"github.com/ethereum/go-ethereum/accounts"
    20  	"github.com/ethereum/go-ethereum/accounts/keystore"
    21  	"github.com/ethereum/go-ethereum/crypto"
    22  )
    23  
    24  func (u *User) keystore() (ks *keystore.KeyStore, err error) {
    25  	if u == nil || u.Email == "" {
    26  		return nil, fmt.Errorf("user has not been initialized")
    27  	}
    28  
    29  	if u.KeyStore == "" {
    30  		u.KeyStore = filepath.Join(dir, "u", u.Email, "k")
    31  	}
    32  
    33  	path, err := filepath.Abs(u.KeyStore)
    34  	if err != nil {
    35  		return
    36  	}
    37  
    38  	if err = ensureDir(path); err != nil {
    39  		return
    40  	}
    41  	u.KeyStore = path
    42  
    43  	return keystore.NewKeyStore(path, keystore.StandardScryptN, keystore.StandardScryptP), nil
    44  }
    45  
    46  func (u *User) defaultAccount() (acc *accounts.Account, err error) {
    47  	ks, err := u.keystore()
    48  	if err != nil {
    49  		return
    50  	}
    51  
    52  	accs := ks.Accounts()
    53  	if len(accs) == 0 {
    54  		return
    55  	}
    56  
    57  	d := u.defaultSecretFilepath()
    58  	for _, a := range accs {
    59  		if a.URL.Path == d {
    60  			return &a, nil
    61  		}
    62  	}
    63  
    64  	return &accs[0], nil
    65  }
    66  
    67  func (u User) defaultSecretFilepath() string {
    68  	return filepath.Join(u.KeyStore, defaultSecretFile)
    69  }
    70  
    71  // SignerIDFromSecret returns the public address for the User's secret, if any, otherwise an empty string.
    72  func (u *User) SignerIDFromSecret() string {
    73  	if acc, _ := u.defaultAccount(); acc != nil {
    74  		return strings.ToLower(acc.Address.Hex())
    75  	}
    76  	return ""
    77  }
    78  
    79  // OpenSecret opens the user's Web3 Secret Storage JSON file for reading.
    80  func (u *User) OpenSecret() (io.Reader, error) {
    81  	acc, err := u.defaultAccount()
    82  	if err != nil {
    83  		return nil, fmt.Errorf("cannot open the secret storage: %s", err)
    84  	}
    85  	if acc == nil {
    86  		return nil, fmt.Errorf("no secret found")
    87  	}
    88  	return os.Open(acc.URL.Path)
    89  }
    90  
    91  // WriteSecret reads a Web3 Secret Storage JSON file from src and writes to disk into the user's secret storage path.
    92  func (u *User) WriteSecret(src io.Reader) error {
    93  	// ensure keystore is ready
    94  	_, err := u.keystore()
    95  	if err != nil {
    96  		return fmt.Errorf("cannot open the keystore: %s", err)
    97  	}
    98  
    99  	dst, err := os.Create(u.defaultSecretFilepath())
   100  	if err != nil {
   101  		return err
   102  	}
   103  	if _, err := io.Copy(dst, src); err != nil {
   104  		return err
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  // ImportSecret imports the user's secret from a ECDSA private key and,
   111  // if successful, any pre-existing secret will be removed.
   112  // The provided passphrase is used to encrypt the secret.
   113  func (u *User) ImportSecret(privateKey ecdsa.PrivateKey, passphrase string) error {
   114  	ks, err := u.keystore()
   115  	if err != nil {
   116  		return fmt.Errorf("cannot open the keystore: %s", err)
   117  	}
   118  
   119  	// todo(leogr): go-ethereum does not allow to overwrite an existing account,
   120  	// so we need to remove the stored secret if matches.
   121  	// But the previous stored account could be lost, although it can be still recovered
   122  	// by using the provided privateKey.
   123  	acc, err := ks.Find(accounts.Account{
   124  		Address: crypto.PubkeyToAddress(privateKey.PublicKey),
   125  	})
   126  	if err == nil {
   127  		if err := os.Remove(acc.URL.Path); err != nil {
   128  			return err
   129  		}
   130  		ks, _ = u.keystore() // reloads the keystore from disk
   131  	}
   132  
   133  	account, err := ks.ImportECDSA(&privateKey, passphrase)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	// Cleanup other secrets, if any
   139  	for _, a := range ks.Accounts() {
   140  		if a.URL.Path == account.URL.Path {
   141  			continue
   142  		}
   143  		if err := os.Remove(a.URL.Path); err != nil {
   144  			return err
   145  		}
   146  	}
   147  	return nil
   148  }