Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signature verification tool in rosetta-cli #387

Merged
merged 3 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ Example: To validate that the Data API implementation is correct, running the fo
docker run -v "$(pwd):/data" -it [image-name] check:data --configuration-file /data/config.json
```

## Key Sign Tool
Rosetta CLI comes with a handy key sign tool for local testing. Please refer to this [README](./cmd/README.md) on how to use it.

## Updates and Releases

We recommend that you continually update your installation to the latest release as soon as possible. The latest release notes are available in our [Community](https://community.rosetta-api.org) board under the [Release](https://community.rosetta-api.org/c/releases/13) category.
Expand Down
33 changes: 33 additions & 0 deletions cmd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## Key Sign Tool

Rosetta CLI has a key sign tool, which you can use to sign and verify various curves supported
by rosetta-specifications. This should only be used for local development. Never share private keys anywhere.

### Usage
#### Sign
```
rosetta-cli key:sign --configuration-file config.json
```

A sample config file is located [here](../examples/configuration/sign.json)

Required fields includes
- `pub_key`
- `private_key`
- `signing_payload`


#### Verify
```
rosetta-cli key:verify --configuration-file config.json
```
A sample config file is located [here](../examples/configuration/sign.json)

Required fields includes
- `pub_key`
- `signing_payload`
- `signature`


### Troubleshoot
- `account_identifier` field in `signing_payload` field should've a dummy address for providing valid payload.
29 changes: 18 additions & 11 deletions cmd/sign.go → cmd/key_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,50 @@ package cmd
import (
"encoding/hex"
"errors"
"fmt"

"github.com/coinbase/rosetta-sdk-go/keys"
"github.com/fatih/color"
"github.com/spf13/cobra"
)

var (
signCmd = &cobra.Command{
Use: "sign",
keySignCmd = &cobra.Command{
Use: "key:sign",
Short: "Sign an unsigned payload with given private key",
Long: `Sign an unsigned payload with given private key
It supports Keypair specified by https://github.com/coinbase/rosetta-specifications`,
RunE: runSignCmd,
It supports Keypair specified by https://github.com/coinbase/rosetta-specifications
Please provide valid PrivateKey, CurveType, SignaturePayload`,
RunE: runKeySignCmd,
}
)

func runSignCmd(_ *cobra.Command, _ []string) error {
func runKeySignCmd(_ *cobra.Command, _ []string) error {
if Config.Sign == nil {
return errors.New("sign configuration is missing")
}

if len(Config.Sign.PrivateKey) == 0 ||
Config.Sign.PubKey.CurveType == "" ||
Config.Sign.SigningPayload == nil ||
Config.Sign.SigningPayload.SignatureType == "" {
color.Red("invalid sign input")
}

keyPair, err := keys.ImportPrivateKey(Config.Sign.PrivateKey, Config.Sign.PubKey.CurveType)
if err != nil {
fmt.Println(fmt.Errorf("unable to import private keys %#v", err))
color.Red("unable to import private keys %#v", err)
return err
}

err = keyPair.IsValid()
if err != nil {
fmt.Println(fmt.Errorf("keypair invalid with err %#v", err))
color.Red("keypair invalid with err %#v", err)
return err
}

signer, err := keyPair.Signer()
if err != nil {
fmt.Println(fmt.Errorf("signer invalid with err %#v", err))
color.Red("signer invalid with err %#v", err)
return err
}

Expand All @@ -62,11 +69,11 @@ func runSignCmd(_ *cobra.Command, _ []string) error {

sign, err := signer.Sign(signingPayload, signatureType)
if err != nil {
fmt.Println(fmt.Errorf("unable to sign with err %#v", err))
color.Red("unable to sign with err %#v", err)
return err
}

hexSig := hex.EncodeToString(sign.Bytes)
color.Blue(hexSig)
color.Green("Signature: %s", hexSig)
return nil
}
69 changes: 69 additions & 0 deletions cmd/key_verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2023 Coinbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"errors"
"github.com/coinbase/rosetta-sdk-go/keys"
"github.com/fatih/color"
"github.com/spf13/cobra"
)

var (
keyVerifyCmd = &cobra.Command{
Use: "key:verify",
Short: "Verify the signature using the public key",
Long: `Verify the signature using the public key
It supports Keypair specified by https://github.com/coinbase/rosetta-specifications`,
RunE: runKeyVerifyCmd,
}
)

func runKeyVerifyCmd(_ *cobra.Command, _ []string) error {
if Config.Sign == nil {
return errors.New("sign configuration is missing")
}

if len(Config.Sign.Signature.Bytes) == 0 ||
Config.Sign.SigningPayload == nil ||
Config.Sign.SigningPayload.SignatureType == "" ||
Config.Sign.PubKey == nil {
color.Red("invalid verify input")
}

keyPair := keys.KeyPair{
PublicKey: Config.Sign.PubKey,
}

signer, err := keyPair.Signer()
if err != nil {
color.Red("signer invalid with err %#v", err)
return err
}

signature := Config.Sign.Signature
signature.SignatureType = Config.Sign.SigningPayload.SignatureType
signature.SigningPayload = Config.Sign.SigningPayload
signature.PublicKey = Config.Sign.PubKey

err = signer.Verify(signature)
if err != nil {
color.Red("invalid signature with err %#v", err)
return err
}

color.Green("Signature Verified.")
return nil
}
7 changes: 5 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,11 @@ default values.`,
)
rootCmd.AddCommand(checkSpecCmd)

// Sign command
rootCmd.AddCommand(signCmd)
// Key Sign command
rootCmd.AddCommand(keySignCmd)

// Key Verify command
rootCmd.AddCommand(keyVerifyCmd)
}

func initConfig() {
Expand Down
1 change: 1 addition & 0 deletions configuration/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ type SignConfiguration struct {
PubKey *types.PublicKey `json:"pub_key"`
PrivateKey string `json:"private_key"`
SigningPayload *types.SigningPayload `json:"signing_payload"`
Signature *types.Signature `json:"signature,omitempty"`
}

// CheckPerfConfiguration configuration for check perf
Expand Down
8 changes: 7 additions & 1 deletion examples/configuration/sign.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
"private_key": "",
"signing_payload": {
"hex_bytes": "370e74254e8cbaa343af3564901456082ec7af967e45ff24ba061233b1a1b04f",
"signature_type": "ecdsa"
"signature_type": "ecdsa",
"account_identifier": {
"address": "dummy"
}
},
"signature": {
"hex_bytes": "c80547470b7e4d3fc17c988b2244dfebc909b3e9f7fd0c1387763263cc70d16d24f326b9c12ba2ea278164c0b30f128a809585fc503eda43de429aadb9f893ef"
}
}
}