OSDN Git Service

add sign-message and verify-message
authoroysheng <oysheng@bytom.io>
Fri, 27 Apr 2018 03:19:10 +0000 (11:19 +0800)
committeroysheng <oysheng@bytom.io>
Fri, 27 Apr 2018 03:19:10 +0000 (11:19 +0800)
account/accounts.go
api/api.go
api/hsm.go
cmd/bytomcli/commands/bytomcli.go
cmd/bytomcli/commands/key.go

index 8a3f6c3..9f87d53 100644 (file)
@@ -39,9 +39,11 @@ var (
 
 // pre-define errors for supporting bytom errorFormatter
 var (
-       ErrDuplicateAlias = errors.New("duplicate account alias")
-       ErrFindAccount    = errors.New("fail to find account")
-       ErrMarshalAccount = errors.New("failed marshal account")
+       ErrDuplicateAlias  = errors.New("duplicate account alias")
+       ErrFindAccount     = errors.New("fail to find account")
+       ErrMarshalAccount  = errors.New("failed marshal account")
+       ErrInvalidAddress  = errors.New("invalid address")
+       ErrFindCtrlProgram = errors.New("fail to find account control program")
 )
 
 func aliasKey(name string) []byte {
@@ -439,3 +441,49 @@ func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
 
        return cps, nil
 }
+
+// GetAccountByAddress return account by given address
+func (m *Manager) GetAccountByAddress(address string) (*Account, error) {
+       addr, err := common.DecodeAddress(address, &consensus.ActiveNetParams)
+       if err != nil {
+               return nil, err
+       }
+
+       redeemContract := addr.ScriptAddress()
+       program := []byte{}
+       switch addr.(type) {
+       case *common.AddressWitnessPubKeyHash:
+               program, err = vmutil.P2WPKHProgram(redeemContract)
+       case *common.AddressWitnessScriptHash:
+               program, err = vmutil.P2WSHProgram(redeemContract)
+       default:
+               return nil, ErrInvalidAddress
+       }
+       if err != nil {
+               return nil, err
+       }
+
+       var hash [32]byte
+       cp := CtrlProgram{}
+       sha3pool.Sum256(hash[:], program)
+       rawProgram := m.db.Get(ContractKey(hash))
+       if rawProgram == nil {
+               return nil, ErrFindCtrlProgram
+       }
+
+       if err := json.Unmarshal(rawProgram, &cp); err != nil {
+               return nil, err
+       }
+
+       rawAccount := m.db.Get(Key(cp.AccountID))
+       if rawAccount == nil {
+               return nil, ErrFindAccount
+       }
+
+       account := &Account{}
+       if err := json.Unmarshal(rawAccount, account); err != nil {
+               return nil, err
+       }
+
+       return account, nil
+}
index 0a331e1..8514320 100644 (file)
@@ -185,6 +185,9 @@ func (a *API) buildHandler() {
                m.Handle("/delete-key", jsonHandler(a.pseudohsmDeleteKey))
                m.Handle("/reset-key-password", jsonHandler(a.pseudohsmResetPassword))
 
+               m.Handle("/sign-message", jsonHandler(a.pseudohsmSignMsg))
+               m.Handle("/verify-message", jsonHandler(a.pseudohsmVerifyMsg))
+
                m.Handle("/build-transaction", jsonHandler(a.build))
                m.Handle("/sign-transaction", jsonHandler(a.pseudohsmSignTemplates))
                m.Handle("/submit-transaction", jsonHandler(a.submit))
index 40c6e77..85a1693 100644 (file)
@@ -2,12 +2,15 @@ package api
 
 import (
        "context"
+       "encoding/hex"
 
        log "github.com/sirupsen/logrus"
 
        "github.com/bytom/blockchain/pseudohsm"
        "github.com/bytom/blockchain/txbuilder"
+       "github.com/bytom/crypto/ed25519"
        "github.com/bytom/crypto/ed25519/chainkd"
+       "github.com/bytom/errors"
        "github.com/bytom/net/http/httperror"
 )
 
@@ -66,6 +69,7 @@ func (a *API) pseudohsmSignTemplate(ctx context.Context, xpub chainkd.XPub, path
        return a.wallet.Hsm.XSign(xpub, path, data[:], password)
 }
 
+// ResetPasswordResp is response for reset password password
 type ResetPasswordResp struct {
        Changed bool `json:"changed"`
 }
@@ -82,3 +86,54 @@ func (a *API) pseudohsmResetPassword(ctx context.Context, ins struct {
        resp.Changed = true
        return NewSuccessResponse(resp)
 }
+
+// SignMsgResp is response for sign message
+type SignMsgResp struct {
+       Signature string `json:"signature"`
+}
+
+func (a *API) pseudohsmSignMsg(ctx context.Context, ins struct {
+       Address  string `json:"address"`
+       Message  []byte `json:"message"`
+       Password string `json:"password"`
+}) Response {
+       account, err := a.wallet.AccountMgr.GetAccountByAddress(ins.Address)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       if len(account.XPubs) == 0 {
+               return NewErrorResponse(errors.New("account xpubs is nil"))
+       }
+
+       sig, err := a.wallet.Hsm.XSign(account.XPubs[0], nil, ins.Message, ins.Password)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+       return NewSuccessResponse(SignMsgResp{Signature: hex.EncodeToString(sig)})
+}
+
+// VerifyMsgResp is response for verify message
+type VerifyMsgResp struct {
+       VerifyResult bool `json:" result"`
+}
+
+func (a *API) pseudohsmVerifyMsg(ctx context.Context, ins struct {
+       Address   string `json:"address"`
+       Message   []byte `json:"message"`
+       Signature []byte `json:"signature"`
+}) Response {
+       account, err := a.wallet.AccountMgr.GetAccountByAddress(ins.Address)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       if len(account.XPubs) == 0 {
+               return NewErrorResponse(errors.New("account xpubs is nil"))
+       }
+
+       if ed25519.Verify(account.XPubs[0].PublicKey(), ins.Message, ins.Signature) {
+               return NewSuccessResponse(VerifyMsgResp{VerifyResult: true})
+       }
+       return NewSuccessResponse(VerifyMsgResp{VerifyResult: false})
+}
index 61b8461..30dfee9 100644 (file)
@@ -148,6 +148,8 @@ func AddCommands() {
        BytomcliCmd.AddCommand(deleteKeyCmd)
        BytomcliCmd.AddCommand(listKeysCmd)
        BytomcliCmd.AddCommand(resetKeyPwdCmd)
+       BytomcliCmd.AddCommand(signMsgCmd)
+       BytomcliCmd.AddCommand(verifyMsgCmd)
 
        BytomcliCmd.AddCommand(createTransactionFeedCmd)
        BytomcliCmd.AddCommand(listTransactionFeedsCmd)
@@ -183,6 +185,8 @@ func AddTemplateFunc() {
                deleteKeyCmd.Name(),
                listKeysCmd.Name(),
                resetKeyPwdCmd.Name(),
+               signMsgCmd.Name(),
+               verifyMsgCmd.Name(),
 
                buildTransactionCmd.Name(),
                signTransactionCmd.Name(),
index 6eb15b1..6d849f7 100644 (file)
@@ -1,7 +1,9 @@
 package commands
 
 import (
+       "encoding/hex"
        "os"
+
        "github.com/spf13/cobra"
        jww "github.com/spf13/jwalterweatherman"
 
@@ -88,3 +90,47 @@ var resetKeyPwdCmd = &cobra.Command{
                jww.FEEDBACK.Println("Successfully reset key password")
        },
 }
+
+var signMsgCmd = &cobra.Command{
+       Use:   "sign-message <address> <message> <password>",
+       Short: "sign message to generate signature",
+       Args:  cobra.ExactArgs(3),
+       Run: func(cmd *cobra.Command, args []string) {
+               var req = struct {
+                       Address  string `json:"address"`
+                       Message  []byte `json:"message"`
+                       Password string `json:"password"`
+               }{Address: args[0], Message: []byte(args[1]), Password: args[2]}
+
+               data, exitCode := util.ClientCall("/sign-message", &req)
+               if exitCode != util.Success {
+                       os.Exit(exitCode)
+               }
+               printJSON(data)
+       },
+}
+
+var verifyMsgCmd = &cobra.Command{
+       Use:   "verify-message <address> <message> <signature>",
+       Short: "verify signature for specified message",
+       Args:  cobra.ExactArgs(3),
+       Run: func(cmd *cobra.Command, args []string) {
+               signature, err := hex.DecodeString(args[2])
+               if err != nil {
+                       jww.ERROR.Println("convert signature error:", err)
+                       os.Exit(util.ErrLocalExe)
+               }
+
+               var req = struct {
+                       Address   string `json:"address"`
+                       Message   []byte `json:"message"`
+                       Signature []byte `json:"signature"`
+               }{Address: args[0], Message: []byte(args[1]), Signature: signature}
+
+               data, exitCode := util.ClientCall("/verify-message", &req)
+               if exitCode != util.Success {
+                       os.Exit(exitCode)
+               }
+               printJSON(data)
+       },
+}