github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/cmd/clef/main.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // signer is a utility that can be used so sign transactions and
    18  // arbitrary data.
    19  package main
    20  
    21  import (
    22  	"bufio"
    23  	"context"
    24  	"crypto/rand"
    25  	"crypto/sha256"
    26  	"encoding/hex"
    27  	"encoding/json"
    28  	"fmt"
    29  	"io"
    30  	"io/ioutil"
    31  	"math/big"
    32  	"os"
    33  	"os/signal"
    34  	"os/user"
    35  	"path/filepath"
    36  	"runtime"
    37  	"strings"
    38  
    39  	"github.com/ethereum/go-ethereum/accounts"
    40  	"github.com/ethereum/go-ethereum/accounts/keystore"
    41  	"github.com/ethereum/go-ethereum/cmd/utils"
    42  	"github.com/ethereum/go-ethereum/common"
    43  	"github.com/ethereum/go-ethereum/common/hexutil"
    44  	"github.com/ethereum/go-ethereum/console"
    45  	"github.com/ethereum/go-ethereum/core/types"
    46  	"github.com/ethereum/go-ethereum/crypto"
    47  	"github.com/ethereum/go-ethereum/internal/ethapi"
    48  	"github.com/ethereum/go-ethereum/log"
    49  	"github.com/ethereum/go-ethereum/node"
    50  	"github.com/ethereum/go-ethereum/params"
    51  	"github.com/ethereum/go-ethereum/rlp"
    52  	"github.com/ethereum/go-ethereum/rpc"
    53  	"github.com/ethereum/go-ethereum/signer/core"
    54  	"github.com/ethereum/go-ethereum/signer/rules"
    55  	"github.com/ethereum/go-ethereum/signer/storage"
    56  	"gopkg.in/urfave/cli.v1"
    57  )
    58  
    59  const legalWarning = `
    60  WARNING! 
    61  
    62  Clef is alpha software, and not yet publically released. This software has _not_ been audited, and there
    63  are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software
    64  unless you agree to take full responsibility for doing so, and know what you are doing. 
    65  
    66  TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE! 
    67  
    68  `
    69  
    70  var (
    71  	logLevelFlag = cli.IntFlag{
    72  		Name:  "loglevel",
    73  		Value: 4,
    74  		Usage: "log level to emit to the screen",
    75  	}
    76  	advancedMode = cli.BoolFlag{
    77  		Name:  "advanced",
    78  		Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off",
    79  	}
    80  	keystoreFlag = cli.StringFlag{
    81  		Name:  "keystore",
    82  		Value: filepath.Join(node.DefaultDataDir(), "keystore"),
    83  		Usage: "Directory for the keystore",
    84  	}
    85  	configdirFlag = cli.StringFlag{
    86  		Name:  "configdir",
    87  		Value: DefaultConfigDir(),
    88  		Usage: "Directory for Clef configuration",
    89  	}
    90  	chainIdFlag = cli.Int64Flag{
    91  		Name:  "chainid",
    92  		Value: params.MainnetChainConfig.ChainID.Int64(),
    93  		Usage: "Chain id to use for signing (1=mainnet, 3=ropsten, 4=rinkeby, 5=Goerli)",
    94  	}
    95  	rpcPortFlag = cli.IntFlag{
    96  		Name:  "rpcport",
    97  		Usage: "HTTP-RPC server listening port",
    98  		Value: node.DefaultHTTPPort + 5,
    99  	}
   100  	signerSecretFlag = cli.StringFlag{
   101  		Name:  "signersecret",
   102  		Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash",
   103  	}
   104  	dBFlag = cli.StringFlag{
   105  		Name:  "4bytedb",
   106  		Usage: "File containing 4byte-identifiers",
   107  		Value: "./4byte.json",
   108  	}
   109  	customDBFlag = cli.StringFlag{
   110  		Name:  "4bytedb-custom",
   111  		Usage: "File used for writing new 4byte-identifiers submitted via API",
   112  		Value: "./4byte-custom.json",
   113  	}
   114  	auditLogFlag = cli.StringFlag{
   115  		Name:  "auditlog",
   116  		Usage: "File used to emit audit logs. Set to \"\" to disable",
   117  		Value: "audit.log",
   118  	}
   119  	ruleFlag = cli.StringFlag{
   120  		Name:  "rules",
   121  		Usage: "Enable rule-engine",
   122  		Value: "rules.json",
   123  	}
   124  	stdiouiFlag = cli.BoolFlag{
   125  		Name: "stdio-ui",
   126  		Usage: "Use STDIN/STDOUT as a channel for an external UI. " +
   127  			"This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " +
   128  			"interface, and can be used when Clef is started by an external process.",
   129  	}
   130  	testFlag = cli.BoolFlag{
   131  		Name:  "stdio-ui-test",
   132  		Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.",
   133  	}
   134  	app         = cli.NewApp()
   135  	initCommand = cli.Command{
   136  		Action:    utils.MigrateFlags(initializeSecrets),
   137  		Name:      "init",
   138  		Usage:     "Initialize the signer, generate secret storage",
   139  		ArgsUsage: "",
   140  		Flags: []cli.Flag{
   141  			logLevelFlag,
   142  			configdirFlag,
   143  		},
   144  		Description: `
   145  The init command generates a master seed which Clef can use to store credentials and data needed for 
   146  the rule-engine to work.`,
   147  	}
   148  	attestCommand = cli.Command{
   149  		Action:    utils.MigrateFlags(attestFile),
   150  		Name:      "attest",
   151  		Usage:     "Attest that a js-file is to be used",
   152  		ArgsUsage: "<sha256sum>",
   153  		Flags: []cli.Flag{
   154  			logLevelFlag,
   155  			configdirFlag,
   156  			signerSecretFlag,
   157  		},
   158  		Description: `
   159  The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of 
   160  incoming requests. 
   161  
   162  Whenever you make an edit to the rule file, you need to use attestation to tell 
   163  Clef that the file is 'safe' to execute.`,
   164  	}
   165  
   166  	setCredentialCommand = cli.Command{
   167  		Action:    utils.MigrateFlags(setCredential),
   168  		Name:      "setpw",
   169  		Usage:     "Store a credential for a keystore file",
   170  		ArgsUsage: "<address>",
   171  		Flags: []cli.Flag{
   172  			logLevelFlag,
   173  			configdirFlag,
   174  			signerSecretFlag,
   175  		},
   176  		Description: `
   177  The setpw command stores a password for a given address (keyfile). If you enter a blank passphrase, it will 
   178  remove any stored credential for that address (keyfile)
   179  `}
   180  	gendocCommand = cli.Command{
   181  		Action: GenDoc,
   182  		Name:   "gendoc",
   183  		Usage:  "Generate documentation about json-rpc format",
   184  		Description: `
   185  The gendoc generates example structures of the json-rpc communication types.
   186  `}
   187  )
   188  
   189  func init() {
   190  	app.Name = "Clef"
   191  	app.Usage = "Manage Ethereum account operations"
   192  	app.Flags = []cli.Flag{
   193  		logLevelFlag,
   194  		keystoreFlag,
   195  		configdirFlag,
   196  		chainIdFlag,
   197  		utils.LightKDFFlag,
   198  		utils.NoUSBFlag,
   199  		utils.RPCListenAddrFlag,
   200  		utils.RPCVirtualHostsFlag,
   201  		utils.IPCDisabledFlag,
   202  		utils.IPCPathFlag,
   203  		utils.RPCEnabledFlag,
   204  		rpcPortFlag,
   205  		signerSecretFlag,
   206  		dBFlag,
   207  		customDBFlag,
   208  		auditLogFlag,
   209  		ruleFlag,
   210  		stdiouiFlag,
   211  		testFlag,
   212  		advancedMode,
   213  	}
   214  	app.Action = signer
   215  	app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, gendocCommand}
   216  
   217  }
   218  func main() {
   219  	if err := app.Run(os.Args); err != nil {
   220  		fmt.Fprintln(os.Stderr, err)
   221  		os.Exit(1)
   222  	}
   223  }
   224  
   225  func initializeSecrets(c *cli.Context) error {
   226  	if err := initialize(c); err != nil {
   227  		return err
   228  	}
   229  	configDir := c.GlobalString(configdirFlag.Name)
   230  
   231  	masterSeed := make([]byte, 256)
   232  	num, err := io.ReadFull(rand.Reader, masterSeed)
   233  	if err != nil {
   234  		return err
   235  	}
   236  	if num != len(masterSeed) {
   237  		return fmt.Errorf("failed to read enough random")
   238  	}
   239  
   240  	n, p := keystore.StandardScryptN, keystore.StandardScryptP
   241  	if c.GlobalBool(utils.LightKDFFlag.Name) {
   242  		n, p = keystore.LightScryptN, keystore.LightScryptP
   243  	}
   244  	text := "The master seed of clef is locked with a password. Please give a password. Do not forget this password."
   245  	var password string
   246  	for {
   247  		password = getPassPhrase(text, true)
   248  		if err := core.ValidatePasswordFormat(password); err != nil {
   249  			fmt.Printf("invalid password: %v\n", err)
   250  		} else {
   251  			break
   252  		}
   253  	}
   254  	cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p)
   255  	if err != nil {
   256  		return fmt.Errorf("failed to encrypt master seed: %v", err)
   257  	}
   258  
   259  	err = os.Mkdir(configDir, 0700)
   260  	if err != nil && !os.IsExist(err) {
   261  		return err
   262  	}
   263  	location := filepath.Join(configDir, "masterseed.json")
   264  	if _, err := os.Stat(location); err == nil {
   265  		return fmt.Errorf("file %v already exists, will not overwrite", location)
   266  	}
   267  	err = ioutil.WriteFile(location, cipherSeed, 0400)
   268  	if err != nil {
   269  		return err
   270  	}
   271  	fmt.Printf("A master seed has been generated into %s\n", location)
   272  	fmt.Printf(`
   273  This is required to be able to store credentials, such as : 
   274  * Passwords for keystores (used by rule engine)
   275  * Storage for javascript rules
   276  * Hash of rule-file
   277  
   278  You should treat that file with utmost secrecy, and make a backup of it. 
   279  NOTE: This file does not contain your accounts. Those need to be backed up separately!
   280  
   281  `)
   282  	return nil
   283  }
   284  func attestFile(ctx *cli.Context) error {
   285  	if len(ctx.Args()) < 1 {
   286  		utils.Fatalf("This command requires an argument.")
   287  	}
   288  	if err := initialize(ctx); err != nil {
   289  		return err
   290  	}
   291  
   292  	stretchedKey, err := readMasterKey(ctx, nil)
   293  	if err != nil {
   294  		utils.Fatalf(err.Error())
   295  	}
   296  	configDir := ctx.GlobalString(configdirFlag.Name)
   297  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   298  	confKey := crypto.Keccak256([]byte("config"), stretchedKey)
   299  
   300  	// Initialize the encrypted storages
   301  	configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey)
   302  	val := ctx.Args().First()
   303  	configStorage.Put("ruleset_sha256", val)
   304  	log.Info("Ruleset attestation updated", "sha256", val)
   305  	return nil
   306  }
   307  
   308  func setCredential(ctx *cli.Context) error {
   309  	if len(ctx.Args()) < 1 {
   310  		utils.Fatalf("This command requires an address to be passed as an argument.")
   311  	}
   312  	if err := initialize(ctx); err != nil {
   313  		return err
   314  	}
   315  
   316  	address := ctx.Args().First()
   317  	password := getPassPhrase("Enter a passphrase to store with this address.", true)
   318  
   319  	stretchedKey, err := readMasterKey(ctx, nil)
   320  	if err != nil {
   321  		utils.Fatalf(err.Error())
   322  	}
   323  	configDir := ctx.GlobalString(configdirFlag.Name)
   324  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   325  	pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
   326  
   327  	// Initialize the encrypted storages
   328  	pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
   329  	pwStorage.Put(address, password)
   330  	log.Info("Credential store updated", "key", address)
   331  	return nil
   332  }
   333  
   334  func initialize(c *cli.Context) error {
   335  	// Set up the logger to print everything
   336  	logOutput := os.Stdout
   337  	if c.GlobalBool(stdiouiFlag.Name) {
   338  		logOutput = os.Stderr
   339  		// If using the stdioui, we can't do the 'confirm'-flow
   340  		fmt.Fprintf(logOutput, legalWarning)
   341  	} else {
   342  		if !confirm(legalWarning) {
   343  			return fmt.Errorf("aborted by user")
   344  		}
   345  	}
   346  
   347  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true))))
   348  	return nil
   349  }
   350  
   351  func signer(c *cli.Context) error {
   352  	if err := initialize(c); err != nil {
   353  		return err
   354  	}
   355  	var (
   356  		ui core.UIClientAPI
   357  	)
   358  	if c.GlobalBool(stdiouiFlag.Name) {
   359  		log.Info("Using stdin/stdout as UI-channel")
   360  		ui = core.NewStdIOUI()
   361  	} else {
   362  		log.Info("Using CLI as UI-channel")
   363  		ui = core.NewCommandlineUI()
   364  	}
   365  	fourByteDb := c.GlobalString(dBFlag.Name)
   366  	fourByteLocal := c.GlobalString(customDBFlag.Name)
   367  	db, err := core.NewAbiDBFromFiles(fourByteDb, fourByteLocal)
   368  	if err != nil {
   369  		utils.Fatalf(err.Error())
   370  	}
   371  	log.Info("Loaded 4byte db", "signatures", db.Size(), "file", fourByteDb, "local", fourByteLocal)
   372  
   373  	var (
   374  		api core.ExternalAPI
   375  	)
   376  
   377  	configDir := c.GlobalString(configdirFlag.Name)
   378  	if stretchedKey, err := readMasterKey(c, ui); err != nil {
   379  		log.Info("No master seed provided, rules disabled", "error", err)
   380  	} else {
   381  
   382  		if err != nil {
   383  			utils.Fatalf(err.Error())
   384  		}
   385  		vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   386  
   387  		// Generate domain specific keys
   388  		pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
   389  		jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey)
   390  		confkey := crypto.Keccak256([]byte("config"), stretchedKey)
   391  
   392  		// Initialize the encrypted storages
   393  		pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
   394  		jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
   395  		configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
   396  
   397  		//Do we have a rule-file?
   398  		ruleJS, err := ioutil.ReadFile(c.GlobalString(ruleFlag.Name))
   399  		if err != nil {
   400  			log.Info("Could not load rulefile, rules not enabled", "file", "rulefile")
   401  		} else {
   402  			hasher := sha256.New()
   403  			hasher.Write(ruleJS)
   404  			shasum := hasher.Sum(nil)
   405  			storedShasum := configStorage.Get("ruleset_sha256")
   406  			if storedShasum != hex.EncodeToString(shasum) {
   407  				log.Info("Could not validate ruleset hash, rules not enabled", "got", hex.EncodeToString(shasum), "expected", storedShasum)
   408  			} else {
   409  				// Initialize rules
   410  				ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage, pwStorage)
   411  				if err != nil {
   412  					utils.Fatalf(err.Error())
   413  				}
   414  				ruleEngine.Init(string(ruleJS))
   415  				ui = ruleEngine
   416  				log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
   417  			}
   418  		}
   419  	}
   420  	var (
   421  		chainId  = c.GlobalInt64(chainIdFlag.Name)
   422  		ksLoc    = c.GlobalString(keystoreFlag.Name)
   423  		lightKdf = c.GlobalBool(utils.LightKDFFlag.Name)
   424  		advanced = c.GlobalBool(advancedMode.Name)
   425  		nousb    = c.GlobalBool(utils.NoUSBFlag.Name)
   426  	)
   427  	log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
   428  		"light-kdf", lightKdf, "advanced", advanced)
   429  	am := core.StartClefAccountManager(ksLoc, nousb, lightKdf)
   430  	apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced)
   431  
   432  	// Establish the bidirectional communication, by creating a new UI backend and registering
   433  	// it with the UI.
   434  	ui.RegisterUIServer(core.NewUIServerAPI(apiImpl))
   435  	api = apiImpl
   436  	// Audit logging
   437  	if logfile := c.GlobalString(auditLogFlag.Name); logfile != "" {
   438  		api, err = core.NewAuditLogger(logfile, api)
   439  		if err != nil {
   440  			utils.Fatalf(err.Error())
   441  		}
   442  		log.Info("Audit logs configured", "file", logfile)
   443  	}
   444  	// register signer API with server
   445  	var (
   446  		extapiURL = "n/a"
   447  		ipcapiURL = "n/a"
   448  	)
   449  	rpcAPI := []rpc.API{
   450  		{
   451  			Namespace: "account",
   452  			Public:    true,
   453  			Service:   api,
   454  			Version:   "1.0"},
   455  	}
   456  	if c.GlobalBool(utils.RPCEnabledFlag.Name) {
   457  
   458  		vhosts := splitAndTrim(c.GlobalString(utils.RPCVirtualHostsFlag.Name))
   459  		cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name))
   460  
   461  		// start http server
   462  		httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
   463  		listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.DefaultHTTPTimeouts)
   464  		if err != nil {
   465  			utils.Fatalf("Could not start RPC api: %v", err)
   466  		}
   467  		extapiURL = fmt.Sprintf("http://%s", httpEndpoint)
   468  		log.Info("HTTP endpoint opened", "url", extapiURL)
   469  
   470  		defer func() {
   471  			listener.Close()
   472  			log.Info("HTTP endpoint closed", "url", httpEndpoint)
   473  		}()
   474  
   475  	}
   476  	if !c.GlobalBool(utils.IPCDisabledFlag.Name) {
   477  		if c.IsSet(utils.IPCPathFlag.Name) {
   478  			ipcapiURL = c.GlobalString(utils.IPCPathFlag.Name)
   479  		} else {
   480  			ipcapiURL = filepath.Join(configDir, "clef.ipc")
   481  		}
   482  
   483  		listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
   484  		if err != nil {
   485  			utils.Fatalf("Could not start IPC api: %v", err)
   486  		}
   487  		log.Info("IPC endpoint opened", "url", ipcapiURL)
   488  		defer func() {
   489  			listener.Close()
   490  			log.Info("IPC endpoint closed", "url", ipcapiURL)
   491  		}()
   492  
   493  	}
   494  
   495  	if c.GlobalBool(testFlag.Name) {
   496  		log.Info("Performing UI test")
   497  		go testExternalUI(apiImpl)
   498  	}
   499  	ui.OnSignerStartup(core.StartupInfo{
   500  		Info: map[string]interface{}{
   501  			"extapi_version": core.ExternalAPIVersion,
   502  			"intapi_version": core.InternalAPIVersion,
   503  			"extapi_http":    extapiURL,
   504  			"extapi_ipc":     ipcapiURL,
   505  		},
   506  	})
   507  
   508  	abortChan := make(chan os.Signal)
   509  	signal.Notify(abortChan, os.Interrupt)
   510  
   511  	sig := <-abortChan
   512  	log.Info("Exiting...", "signal", sig)
   513  
   514  	return nil
   515  }
   516  
   517  // splitAndTrim splits input separated by a comma
   518  // and trims excessive white space from the substrings.
   519  func splitAndTrim(input string) []string {
   520  	result := strings.Split(input, ",")
   521  	for i, r := range result {
   522  		result[i] = strings.TrimSpace(r)
   523  	}
   524  	return result
   525  }
   526  
   527  // DefaultConfigDir is the default config directory to use for the vaults and other
   528  // persistence requirements.
   529  func DefaultConfigDir() string {
   530  	// Try to place the data folder in the user's home dir
   531  	home := homeDir()
   532  	if home != "" {
   533  		if runtime.GOOS == "darwin" {
   534  			return filepath.Join(home, "Library", "Signer")
   535  		} else if runtime.GOOS == "windows" {
   536  			appdata := os.Getenv("APPDATA")
   537  			if appdata != "" {
   538  				return filepath.Join(appdata, "Signer")
   539  			} else {
   540  				return filepath.Join(home, "AppData", "Roaming", "Signer")
   541  			}
   542  		} else {
   543  			return filepath.Join(home, ".clef")
   544  		}
   545  	}
   546  	// As we cannot guess a stable location, return empty and handle later
   547  	return ""
   548  }
   549  
   550  func homeDir() string {
   551  	if home := os.Getenv("HOME"); home != "" {
   552  		return home
   553  	}
   554  	if usr, err := user.Current(); err == nil {
   555  		return usr.HomeDir
   556  	}
   557  	return ""
   558  }
   559  func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
   560  	var (
   561  		file      string
   562  		configDir = ctx.GlobalString(configdirFlag.Name)
   563  	)
   564  	if ctx.GlobalIsSet(signerSecretFlag.Name) {
   565  		file = ctx.GlobalString(signerSecretFlag.Name)
   566  	} else {
   567  		file = filepath.Join(configDir, "masterseed.json")
   568  	}
   569  	if err := checkFile(file); err != nil {
   570  		return nil, err
   571  	}
   572  	cipherKey, err := ioutil.ReadFile(file)
   573  	if err != nil {
   574  		return nil, err
   575  	}
   576  	var password string
   577  	// If ui is not nil, get the password from ui.
   578  	if ui != nil {
   579  		resp, err := ui.OnInputRequired(core.UserInputRequest{
   580  			Title:      "Master Password",
   581  			Prompt:     "Please enter the password to decrypt the master seed",
   582  			IsPassword: true})
   583  		if err != nil {
   584  			return nil, err
   585  		}
   586  		password = resp.Text
   587  	} else {
   588  		password = getPassPhrase("Decrypt master seed of clef", false)
   589  	}
   590  	masterSeed, err := decryptSeed(cipherKey, password)
   591  	if err != nil {
   592  		return nil, fmt.Errorf("failed to decrypt the master seed of clef")
   593  	}
   594  	if len(masterSeed) < 256 {
   595  		return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed))
   596  	}
   597  
   598  	// Create vault location
   599  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10]))
   600  	err = os.Mkdir(vaultLocation, 0700)
   601  	if err != nil && !os.IsExist(err) {
   602  		return nil, err
   603  	}
   604  	return masterSeed, nil
   605  }
   606  
   607  // checkFile is a convenience function to check if a file
   608  // * exists
   609  // * is mode 0400
   610  func checkFile(filename string) error {
   611  	info, err := os.Stat(filename)
   612  	if err != nil {
   613  		return fmt.Errorf("failed stat on %s: %v", filename, err)
   614  	}
   615  	// Check the unix permission bits
   616  	if info.Mode().Perm()&0377 != 0 {
   617  		return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String())
   618  	}
   619  	return nil
   620  }
   621  
   622  // confirm displays a text and asks for user confirmation
   623  func confirm(text string) bool {
   624  	fmt.Printf(text)
   625  	fmt.Printf("\nEnter 'ok' to proceed:\n>")
   626  
   627  	text, err := bufio.NewReader(os.Stdin).ReadString('\n')
   628  	if err != nil {
   629  		log.Crit("Failed to read user input", "err", err)
   630  	}
   631  
   632  	if text := strings.TrimSpace(text); text == "ok" {
   633  		return true
   634  	}
   635  	return false
   636  }
   637  
   638  func testExternalUI(api *core.SignerAPI) {
   639  
   640  	ctx := context.WithValue(context.Background(), "remote", "clef binary")
   641  	ctx = context.WithValue(ctx, "scheme", "in-proc")
   642  	ctx = context.WithValue(ctx, "local", "main")
   643  
   644  	errs := make([]string, 0)
   645  
   646  	api.UI.ShowInfo("Testing 'ShowInfo'")
   647  	api.UI.ShowError("Testing 'ShowError'")
   648  
   649  	checkErr := func(method string, err error) {
   650  		if err != nil && err != core.ErrRequestDenied {
   651  			errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error()))
   652  		}
   653  	}
   654  	var err error
   655  
   656  	cliqueHeader := types.Header{
   657  		common.HexToHash("0000H45H"),
   658  		common.HexToHash("0000H45H"),
   659  		common.HexToAddress("0000H45H"),
   660  		common.HexToHash("0000H00H"),
   661  		common.HexToHash("0000H45H"),
   662  		common.HexToHash("0000H45H"),
   663  		types.Bloom{},
   664  		big.NewInt(1337),
   665  		big.NewInt(1337),
   666  		1338,
   667  		1338,
   668  		big.NewInt(1338),
   669  		[]byte("Extra data Extra data Extra data  Extra data  Extra data  Extra data  Extra data Extra data"),
   670  		common.HexToHash("0x0000H45H"),
   671  		types.BlockNonce{},
   672  	}
   673  	cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
   674  	if err != nil {
   675  		utils.Fatalf("Should not error: %v", err)
   676  	}
   677  	addr, err := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
   678  	if err != nil {
   679  		utils.Fatalf("Should not error: %v", err)
   680  	}
   681  	_, err = api.SignData(ctx, "application/clique", *addr, cliqueRlp)
   682  	checkErr("SignData", err)
   683  
   684  	_, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil)
   685  	checkErr("SignTransaction", err)
   686  	_, err = api.SignData(ctx, "text/plain", common.MixedcaseAddress{}, common.Hex2Bytes("01020304"))
   687  	checkErr("SignData", err)
   688  	//_, err = api.SignTypedData(ctx, common.MixedcaseAddress{}, core.TypedData{})
   689  	//checkErr("SignTypedData", err)
   690  	_, err = api.List(ctx)
   691  	checkErr("List", err)
   692  	_, err = api.New(ctx)
   693  	checkErr("New", err)
   694  
   695  	api.UI.ShowInfo("Tests completed")
   696  
   697  	if len(errs) > 0 {
   698  		log.Error("Got errors")
   699  		for _, e := range errs {
   700  			log.Error(e)
   701  		}
   702  	} else {
   703  		log.Info("No errors")
   704  	}
   705  }
   706  
   707  // getPassPhrase retrieves the password associated with clef, either fetched
   708  // from a list of preloaded passphrases, or requested interactively from the user.
   709  // TODO: there are many `getPassPhrase` functions, it will be better to abstract them into one.
   710  func getPassPhrase(prompt string, confirmation bool) string {
   711  	fmt.Println(prompt)
   712  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   713  	if err != nil {
   714  		utils.Fatalf("Failed to read passphrase: %v", err)
   715  	}
   716  	if confirmation {
   717  		confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
   718  		if err != nil {
   719  			utils.Fatalf("Failed to read passphrase confirmation: %v", err)
   720  		}
   721  		if password != confirm {
   722  			utils.Fatalf("Passphrases do not match")
   723  		}
   724  	}
   725  	return password
   726  }
   727  
   728  type encryptedSeedStorage struct {
   729  	Description string              `json:"description"`
   730  	Version     int                 `json:"version"`
   731  	Params      keystore.CryptoJSON `json:"params"`
   732  }
   733  
   734  // encryptSeed uses a similar scheme as the keystore uses, but with a different wrapping,
   735  // to encrypt the master seed
   736  func encryptSeed(seed []byte, auth []byte, scryptN, scryptP int) ([]byte, error) {
   737  	cryptoStruct, err := keystore.EncryptDataV3(seed, auth, scryptN, scryptP)
   738  	if err != nil {
   739  		return nil, err
   740  	}
   741  	return json.Marshal(&encryptedSeedStorage{"Clef seed", 1, cryptoStruct})
   742  }
   743  
   744  // decryptSeed decrypts the master seed
   745  func decryptSeed(keyjson []byte, auth string) ([]byte, error) {
   746  	var encSeed encryptedSeedStorage
   747  	if err := json.Unmarshal(keyjson, &encSeed); err != nil {
   748  		return nil, err
   749  	}
   750  	if encSeed.Version != 1 {
   751  		log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version))
   752  	}
   753  	seed, err := keystore.DecryptDataV3(encSeed.Params, auth)
   754  	if err != nil {
   755  		return nil, err
   756  	}
   757  	return seed, err
   758  }
   759  
   760  // GenDoc outputs examples of all structures used in json-rpc communication
   761  func GenDoc(ctx *cli.Context) {
   762  
   763  	var (
   764  		a    = common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef")
   765  		b    = common.HexToAddress("0x1111111122222222222233333333334444444444")
   766  		meta = core.Metadata{
   767  			Scheme:    "http",
   768  			Local:     "localhost:8545",
   769  			Origin:    "www.malicious.ru",
   770  			Remote:    "localhost:9999",
   771  			UserAgent: "Firefox 3.2",
   772  		}
   773  		output []string
   774  		add    = func(name, desc string, v interface{}) {
   775  			if data, err := json.MarshalIndent(v, "", "  "); err == nil {
   776  				output = append(output, fmt.Sprintf("### %s\n\n%s\n\nExample:\n```json\n%s\n```", name, desc, data))
   777  			} else {
   778  				log.Error("Error generating output", err)
   779  			}
   780  		}
   781  	)
   782  
   783  	{ // Sign plain text request
   784  		desc := "SignDataRequest contains information about a pending request to sign some data. " +
   785  			"The data to be signed can be of various types, defined by content-type. Clef has done most " +
   786  			"of the work in canonicalizing and making sense of the data, and it's up to the UI to present" +
   787  			"the user with the contents of the `message`"
   788  		sighash, msg := accounts.TextAndHash([]byte("hello world"))
   789  		message := []*core.NameValueType{{"message", msg, accounts.MimetypeTextPlain}}
   790  
   791  		add("SignDataRequest", desc, &core.SignDataRequest{
   792  			Address:     common.NewMixedcaseAddress(a),
   793  			Meta:        meta,
   794  			ContentType: accounts.MimetypeTextPlain,
   795  			Rawdata:     []byte(msg),
   796  			Message:     message,
   797  			Hash:        sighash})
   798  	}
   799  	{ // Sign plain text response
   800  		add("SignDataResponse - approve", "Response to SignDataRequest",
   801  			&core.SignDataResponse{Password: "apassword", Approved: true})
   802  		add("SignDataResponse - deny", "Response to SignDataRequest",
   803  			&core.SignDataResponse{})
   804  	}
   805  	{ // Sign transaction request
   806  		desc := "SignTxRequest contains information about a pending request to sign a transaction. " +
   807  			"Aside from the transaction itself, there is also a `call_info`-struct. That struct contains " +
   808  			"messages of various types, that the user should be informed of." +
   809  			"\n\n" +
   810  			"As in any request, it's important to consider that the `meta` info also contains untrusted data." +
   811  			"\n\n" +
   812  			"The `transaction` (on input into clef) can have either `data` or `input` -- if both are set, " +
   813  			"they must be identical, otherwise an error is generated. " +
   814  			"However, Clef will always use `data` when passing this struct on (if Clef does otherwise, please file a ticket)"
   815  
   816  		data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04})
   817  		add("SignTxRequest", desc, &core.SignTxRequest{
   818  			Meta: meta,
   819  			Callinfo: []core.ValidationInfo{
   820  				{"Warning", "Something looks odd, show this message as a warning"},
   821  				{"Info", "User should see this aswell"},
   822  			},
   823  			Transaction: core.SendTxArgs{
   824  				Data:     &data,
   825  				Nonce:    0x1,
   826  				Value:    hexutil.Big(*big.NewInt(6)),
   827  				From:     common.NewMixedcaseAddress(a),
   828  				To:       nil,
   829  				GasPrice: hexutil.Big(*big.NewInt(5)),
   830  				Gas:      1000,
   831  				Input:    nil,
   832  			}})
   833  	}
   834  	{ // Sign tx response
   835  		data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01})
   836  		add("SignDataResponse - approve", "Response to SignDataRequest. This response needs to contain the `transaction`"+
   837  			", because the UI is free to make modifications to the transaction.",
   838  			&core.SignTxResponse{Password: "apassword", Approved: true,
   839  				Transaction: core.SendTxArgs{
   840  					Data:     &data,
   841  					Nonce:    0x4,
   842  					Value:    hexutil.Big(*big.NewInt(6)),
   843  					From:     common.NewMixedcaseAddress(a),
   844  					To:       nil,
   845  					GasPrice: hexutil.Big(*big.NewInt(5)),
   846  					Gas:      1000,
   847  					Input:    nil,
   848  				}})
   849  		add("SignDataResponse - deny", "Response to SignDataRequest. When denying a request, there's no need to "+
   850  			"provide the transaction in return",
   851  			&core.SignDataResponse{})
   852  	}
   853  	{ // WHen a signed tx is ready to go out
   854  		desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" +
   855  			"\n\n" +
   856  			"This occurs _after_ successful completion of the entire signing procedure, but right before the signed " +
   857  			"transaction is passed to the external caller. This method (and data) can be used by the UI to signal " +
   858  			"to the user that the transaction was signed, but it is primarily useful for ruleset implementations." +
   859  			"\n\n" +
   860  			"A ruleset that implements a rate limitation needs to know what transactions are sent out to the external " +
   861  			"interface. By hooking into this methods, the ruleset can maintain track of that count." +
   862  			"\n\n" +
   863  			"**OBS:** Note that if an attacker can restore your `clef` data to a previous point in time" +
   864  			" (e.g through a backup), the attacker can reset such windows, even if he/she is unable to decrypt the content. " +
   865  			"\n\n" +
   866  			"The `OnApproved` method cannot be responded to, it's purely informative"
   867  
   868  		rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed")
   869  		var tx types.Transaction
   870  		rlp.DecodeBytes(rlpdata, &tx)
   871  		add("OnApproved - SignTransactionResult", desc, &ethapi.SignTransactionResult{Raw: rlpdata, Tx: &tx})
   872  
   873  	}
   874  	{ // User input
   875  		add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)",
   876  			&core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"})
   877  		add("UserInputResponse", "Response to SignDataRequest",
   878  			&core.UserInputResponse{Text: "The textual response from user"})
   879  	}
   880  	{ // List request
   881  		add("ListRequest", "Sent when a request has been made to list addresses. The UI is provided with the "+
   882  			"full `account`s, including local directory names. Note: this information is not passed back to the external caller, "+
   883  			"who only sees the `address`es. ",
   884  			&core.ListRequest{
   885  				Meta: meta,
   886  				Accounts: []accounts.Account{
   887  					{a, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}},
   888  					{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
   889  			})
   890  
   891  		add("UserInputResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
   892  			"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
   893  			&core.ListResponse{
   894  				Accounts: []accounts.Account{
   895  					{common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"), accounts.URL{Path: ".. ignored .."}},
   896  					{common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"), accounts.URL{}},
   897  				}})
   898  	}
   899  
   900  	fmt.Println(`## UI Client interface
   901  
   902  These data types are defined in the channel between clef and the UI`)
   903  	for _, elem := range output {
   904  		fmt.Println(elem)
   905  	}
   906  }
   907  
   908  /**
   909  //Create Account
   910  
   911  curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_new","params":["test"],"id":67}' localhost:8550
   912  
   913  // List accounts
   914  
   915  curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_list","params":[""],"id":67}' http://localhost:8550/
   916  
   917  // Make Transaction
   918  // safeSend(0x12)
   919  // 4401a6e40000000000000000000000000000000000000000000000000000000000000012
   920  
   921  // supplied abi
   922  curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x82A2A876D39022B3019932D30Cd9c97ad5616813","gas":"0x333","gasPrice":"0x123","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x10", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"test"],"id":67}' http://localhost:8550/
   923  
   924  // Not supplied
   925  curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x82A2A876D39022B3019932D30Cd9c97ad5616813","gas":"0x333","gasPrice":"0x123","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x10", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"}],"id":67}' http://localhost:8550/
   926  
   927  // Sign data
   928  
   929  curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_sign","params":["0x694267f14675d7e1b9494fd8d72fefe1755710fa","bazonk gaz baz"],"id":67}' http://localhost:8550/
   930  
   931  
   932  **/