From 5e1e5f715218e6c3a28b06c2bdba5b843811a0ac Mon Sep 17 00:00:00 2001 From: earl Date: Sat, 9 Mar 2024 23:24:16 +0100 Subject: [PATCH 1/2] Add a smapp-compatible `wallet sign` subcommand Motivation ---------- smapp has a simple message signing functionality, which uses a wallet's keypair to sign text messages using Ed25519ctx. This functionality is used by, for example, Team24 to authenticate an sm1 address owner. The functionality is also supported by the third-party wallet implementation smeshwallet.com. Description ----------- This change adds a simple `smcli wallet sign` subcommand, which takes a wallet JSON and a message as arguments, signs the message, and outputs a smapp-compatible JSON structure of message, signature, and signer public key. --- cmd/wallet.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/cmd/wallet.go b/cmd/wallet.go index 795e146..324bf74 100644 --- a/cmd/wallet.go +++ b/cmd/wallet.go @@ -1,7 +1,10 @@ package cmd import ( + "crypto" + "crypto/ed25519" "encoding/hex" + "encoding/json" "errors" "fmt" "log" @@ -289,10 +292,58 @@ only child keys).`, }, } +var signCmd = &cobra.Command{ + Use: "sign [wallet file] [message]", + Short: "Signs a message using a wallet's first child key", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + walletFn := args[0] + message := args[1] + + // make sure the file exists + f, err := os.Open(walletFn) + cobra.CheckErr(err) + defer f.Close() + + // get the password + fmt.Print("Enter wallet password: ") + password, err := password.Read(os.Stdin) + fmt.Println() + cobra.CheckErr(err) + + // attempt to read it + wk := wallet.NewKey(wallet.WithPasswordOnly([]byte(password))) + w, err := wk.Open(f, debug) + cobra.CheckErr(err) + + // Sign message using child account 0. + child0 := w.Secrets.Accounts[0] // TODO: flag to select child + sk0 := ed25519.PrivateKey(child0.Private) + sig, err := sk0.Sign(nil, []byte(message), crypto.Hash(0)) + cobra.CheckErr(err) + + // Output signed message in a JSON format compatible with smapp's signing feature. + type signedMessage struct { + Text string `json:"text"` + Signature string `json:"signature"` + PublicKey string `json:"publicKey"` + } + out := signedMessage{ + Text: message, + Signature: "0x" + hex.EncodeToString(sig), + PublicKey: "0x" + hex.EncodeToString(child0.Public), + } + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + enc.Encode(out) + }, +} + func init() { rootCmd.AddCommand(walletCmd) walletCmd.AddCommand(createCmd) walletCmd.AddCommand(readCmd) + walletCmd.AddCommand(signCmd) readCmd.Flags().BoolVarP(&printPrivate, "private", "p", false, "Print private keys") readCmd.Flags().BoolVarP(&printFull, "full", "f", false, "Print full keys (no abbreviation)") readCmd.Flags().BoolVar(&printBase58, "base58", false, "Print keys in base58 (rather than hex)") From 98a0db073938f3f6482c0e43c2a59325f9d49549 Mon Sep 17 00:00:00 2001 From: earl Date: Sat, 9 Mar 2024 23:46:22 +0100 Subject: [PATCH 2/2] Extract wallet opening into helper function --- cmd/wallet.go | 54 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/cmd/wallet.go b/cmd/wallet.go index 324bf74..6c4c681 100644 --- a/cmd/wallet.go +++ b/cmd/wallet.go @@ -45,6 +45,30 @@ var ( hrp string ) +func openWallet(walletFn string) (*wallet.Wallet, error) { + // make sure the file exists + f, err := os.Open(walletFn) + cobra.CheckErr(err) + defer f.Close() + + // get the password + fmt.Print("Enter wallet password: ") + password, err := password.Read(os.Stdin) + fmt.Println() + if err != nil { + return nil, err + } + + // attempt to read it + wk := wallet.NewKey(wallet.WithPasswordOnly([]byte(password))) + w, err := wk.Open(f, debug) + if err != nil { + return nil, err + } + + return w, nil +} + // walletCmd represents the wallet command. var walletCmd = &cobra.Command{ Use: "wallet", @@ -158,20 +182,7 @@ only child keys).`, Run: func(cmd *cobra.Command, args []string) { walletFn := args[0] - // make sure the file exists - f, err := os.Open(walletFn) - cobra.CheckErr(err) - defer f.Close() - - // get the password - fmt.Print("Enter wallet password: ") - password, err := password.Read(os.Stdin) - fmt.Println() - cobra.CheckErr(err) - - // attempt to read it - wk := wallet.NewKey(wallet.WithPasswordOnly([]byte(password))) - w, err := wk.Open(f, debug) + w, err := openWallet(walletFn) cobra.CheckErr(err) widthEnforcer := func(col string, maxLen int) string { @@ -300,20 +311,7 @@ var signCmd = &cobra.Command{ walletFn := args[0] message := args[1] - // make sure the file exists - f, err := os.Open(walletFn) - cobra.CheckErr(err) - defer f.Close() - - // get the password - fmt.Print("Enter wallet password: ") - password, err := password.Read(os.Stdin) - fmt.Println() - cobra.CheckErr(err) - - // attempt to read it - wk := wallet.NewKey(wallet.WithPasswordOnly([]byte(password))) - w, err := wk.Open(f, debug) + w, err := openWallet(walletFn) cobra.CheckErr(err) // Sign message using child account 0.