github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/cmd/smc/securitycmd.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"github.com/SmartMeshFoundation/Spectrum/accounts/keystore"
     8  	"github.com/SmartMeshFoundation/Spectrum/cmd/utils"
     9  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    10  	"github.com/SmartMeshFoundation/Spectrum/node"
    11  	"github.com/pborman/uuid"
    12  	"gopkg.in/urfave/cli.v1"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  )
    17  
    18  const PWD_MIN_LEN = 6
    19  
    20  var (
    21  	security        = new(Security)
    22  	securityCommand = cli.Command{
    23  		Before: func(ctx *cli.Context) error {
    24  			fmt.Println("-----------------------")
    25  			if ctx.Bool(utils.TestnetFlag.Name) {
    26  				fmt.Println("-   TESTNET ")
    27  				os.Setenv("TESTNET", "1")
    28  			} else if ctx.Bool(utils.DevnetFlag.Name) {
    29  				fmt.Println("-   DEVNET ")
    30  				os.Setenv("DEVNET", "1")
    31  			} else {
    32  				fmt.Println("-   MAINNET ")
    33  			}
    34  			security.homeDir = node.DefaultNodekeyDir()
    35  			fmt.Println("home_dir :", security.homeDir)
    36  			fmt.Println("-----------------------")
    37  			security.nodekey = filepath.Join(security.homeDir, "nodekey")
    38  			security.nodekeyPrv = filepath.Join(security.homeDir, "nodekey.prv")
    39  			return nil
    40  		},
    41  		Action:    security.run,
    42  		Name:      "security",
    43  		Usage:     "encrypt / decrypt nodekey, params mutual exclusion, only one choice",
    44  		ArgsUsage: "<TODO>",
    45  		Flags: []cli.Flag{
    46  			cli.BoolFlag{
    47  				Name:  "testnet,t",
    48  				Usage: "",
    49  			},
    50  			cli.BoolFlag{
    51  				Name:        "unlock,l",
    52  				Usage:       "unlock nodekey with password",
    53  				Destination: &security.l,
    54  			},
    55  			cli.BoolFlag{
    56  				Name:        "passwd,p",
    57  				Usage:       "set or reset password",
    58  				Destination: &security.p,
    59  			},
    60  		},
    61  		Category:    "BLOCKCHAIN COMMANDS",
    62  		Description: `TODO`,
    63  	}
    64  )
    65  
    66  type Security struct {
    67  	homeDir, nodekey, nodekeyPrv string
    68  	l, p                         bool
    69  	prv                          *ecdsa.PrivateKey
    70  }
    71  
    72  func (self *Security) run(ctx *cli.Context) error {
    73  	if security.l && security.p {
    74  		fmt.Println("================================================================================")
    75  		fmt.Println(" INPUT : unlock=", security.l, " ; passwd=", security.p)
    76  		fmt.Println(" ERROR : params mutual exclusion, only one choice")
    77  		fmt.Println("================================================================================")
    78  	}
    79  	switch {
    80  	case security.l:
    81  		self.unlockCmd()
    82  	case security.p:
    83  		self.passwdCmd()
    84  	}
    85  	return nil
    86  }
    87  
    88  func (self *Security) unlockCmd() {
    89  	var (
    90  		pwd     string
    91  		counter int
    92  		pwdfile = filepath.Join(security.homeDir, "nodekey.pwd")
    93  	)
    94  	data, err := ioutil.ReadFile(self.nodekeyPrv)
    95  	if err != nil {
    96  		fmt.Println("Please set password first.")
    97  		fmt.Println("ERROR :", err)
    98  	}
    99  	kjson, err := hex.DecodeString(string(data))
   100  	if err != nil {
   101  		fmt.Println("ERROR:", err)
   102  		return
   103  	}
   104  INPUT:
   105  	fmt.Println("Please input password : ")
   106  	fmt.Scanln(&pwd)
   107  	if _, err := keystore.DecryptKey(kjson, pwd); err != nil {
   108  		counter++
   109  		fmt.Println(counter, "❌ Wrong password .")
   110  		if counter < 3 {
   111  			goto INPUT
   112  		}
   113  		return
   114  	}
   115  	os.Remove(pwdfile)
   116  	pwdhex := hex.EncodeToString([]byte(pwd))
   117  	if err := ioutil.WriteFile(pwdfile, []byte(pwdhex), 0666); err == nil {
   118  		fmt.Println("😊 Success.")
   119  	} else {
   120  		fmt.Println("😢 Error :", err)
   121  	}
   122  }
   123  
   124  func (self *Security) passwdCmd() {
   125  	var (
   126  		oldpwd, pwd string
   127  	)
   128  	data, err := ioutil.ReadFile(self.nodekeyPrv)
   129  	if err != nil {
   130  		counter := 0
   131  		// set password and move nodekey to nodekey.prv
   132  		if err := self.loadNodekey(self.nodekey); err != nil {
   133  			fmt.Println("ERROR ::", err)
   134  			return
   135  		}
   136  		defer os.Remove(self.nodekey)
   137  	INPUT:
   138  		fmt.Println("Please input password : ")
   139  		fmt.Scanln(&pwd)
   140  		if len(pwd) < PWD_MIN_LEN {
   141  			counter++
   142  			fmt.Println(counter, "❌ The password length needs to be greater than", PWD_MIN_LEN)
   143  			if counter < 3 {
   144  				goto INPUT
   145  			}
   146  			return
   147  		}
   148  		self.storeKey(self.nodekeyPrv, pwd)
   149  	} else {
   150  		// reset password
   151  		counter := 0
   152  		fmt.Println("Please input old password : ")
   153  		fmt.Scanln(&oldpwd)
   154  		kjson, err := hex.DecodeString(string(data))
   155  		if err != nil {
   156  			fmt.Println("ERROR:", err)
   157  			return
   158  		}
   159  
   160  		key, err := keystore.DecryptKey(kjson, oldpwd)
   161  		if err != nil {
   162  			fmt.Println("ERROR:", err)
   163  			return
   164  		}
   165  	NEWPWD:
   166  		fmt.Println("Please input new password : ")
   167  		fmt.Scanln(&pwd)
   168  		if len(pwd) < PWD_MIN_LEN {
   169  			counter++
   170  			fmt.Println(counter, "❌ The new password length needs to be greater than", PWD_MIN_LEN)
   171  			if counter < 3 {
   172  				goto NEWPWD
   173  			}
   174  			return
   175  		}
   176  		self.prv = key.PrivateKey
   177  		os.Remove(self.nodekeyPrv)
   178  		self.storeKey(self.nodekeyPrv, pwd)
   179  	}
   180  
   181  }
   182  
   183  func (self *Security) loadNodekey(path string) error {
   184  	f, err := os.Open(path)
   185  	if err != nil {
   186  		prv, err := crypto.GenerateKey()
   187  		if err != nil {
   188  			return err
   189  		}
   190  		self.prv = prv
   191  	}
   192  	buf := make([]byte, 512)
   193  	if n, err := f.Read(buf); err == nil {
   194  		k := string(buf[0:n])
   195  		data, _ := hex.DecodeString(k)
   196  		prv, err := crypto.ToECDSA(data)
   197  		if err != nil {
   198  			return err
   199  		}
   200  		self.prv = prv
   201  	}
   202  	return nil
   203  }
   204  
   205  func (self *Security) storeKey(path, passphrase string) {
   206  	id := uuid.NewRandom()
   207  	key := &keystore.Key{
   208  		Id:         id,
   209  		Address:    crypto.PubkeyToAddress(self.prv.PublicKey),
   210  		PrivateKey: self.prv,
   211  	}
   212  	keyjson, _ := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP)
   213  	hexkey := hex.EncodeToString(keyjson)
   214  	if err := ioutil.WriteFile(path, []byte(hexkey), 0666); err == nil {
   215  		fmt.Println("😊 Success.")
   216  	} else {
   217  		fmt.Println("😢 Error :", err)
   218  	}
   219  }