-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
keys
subcommands to sign and verify arbitrary text payloads.
Closes: #4581
- Loading branch information
Alessio Treglia
committed
Sep 18, 2019
1 parent
9c53712
commit b375e1d
Showing
7 changed files
with
206 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package keys | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client/input" | ||
) | ||
|
||
func signCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "sign <name> <filename>", | ||
Short: "Sign a plain text payload with a private key and print the signed document to STDOUT", | ||
Long: `Sign an arbitrary text file with a private key and produce an amino-encoded JSON output. | ||
The signed JSON document could eventually be verified through the 'keys verify' command and will | ||
have the following structure: | ||
{ | ||
"text": original text file contents, | ||
"pub": public key, | ||
"sig": signature | ||
} | ||
`, | ||
Args: cobra.ExactArgs(2), | ||
RunE: runSignCmd, | ||
} | ||
cmd.SetOut(os.Stdout) | ||
return cmd | ||
} | ||
|
||
func runSignCmd(cmd *cobra.Command, args []string) error { | ||
name := args[0] | ||
filename := args[1] | ||
|
||
kb, err := NewKeyBaseFromHomeFlag() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
msg, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
buf := bufio.NewReader(cmd.InOrStdin()) | ||
passphrase, err := input.GetPassword(fmt.Sprintf("Password to sign with '%s':", name), buf) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
sig, pub, err := kb.Sign(name, passphrase, msg) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
out, err := MarshalJSON(signedText{ | ||
Text: string(msg), | ||
Pub: pub, | ||
Sig: sig, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
cmd.Println(string(out)) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package keys | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"testing" | ||
|
||
"github.com/spf13/viper" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/tendermint/tendermint/libs/cli" | ||
|
||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/cosmos/cosmos-sdk/tests" | ||
) | ||
|
||
func Test_runSignCmd(t *testing.T) { | ||
signCmd := signCommand() | ||
// err := runSignCmd(signCmd, []string{"invalid", "invalid"}) | ||
// require.Contains(t, err.Error(), "no such file or directory") | ||
|
||
// Prepare a plain text doc | ||
tmpfile, err := ioutil.TempFile("", "") | ||
require.NoError(t, err) | ||
ioutil.WriteFile(tmpfile.Name(), []byte(`this is | ||
an example`), 0644) | ||
require.NoError(t, err) | ||
tmpfile.Close() | ||
defer os.Remove(tmpfile.Name()) | ||
|
||
// Prepare a key base | ||
// Now add a temporary keybase | ||
kbHome, cleanUp := tests.NewTestCaseDir(t) | ||
defer cleanUp() | ||
viper.Set(flags.FlagHome, kbHome) | ||
viper.Set(cli.OutputFlag, OutputFormatText) | ||
|
||
// Initialise keybase | ||
kb, err := NewKeyBaseFromHomeFlag() | ||
assert.NoError(t, err) | ||
_, err = kb.CreateAccount("key1", tests.TestMnemonic, "", "test1234", 0, 0) | ||
assert.NoError(t, err) | ||
|
||
// Mock standard streams | ||
mockIn, mockOut, _ := tests.ApplyMockIO(signCmd) | ||
mockIn.Reset("test1234\n") | ||
require.NoError(t, runSignCmd(signCmd, []string{"key1", tmpfile.Name()})) | ||
|
||
signedDocBytes := mockOut.Bytes() | ||
var signedDoc signedText | ||
require.NoError(t, UnmarshalJSON(signedDocBytes, &signedDoc)) | ||
|
||
// Prepare a signed doc file | ||
outTmpFile, err := ioutil.TempFile("", "") | ||
require.NoError(t, err) | ||
ioutil.WriteFile(outTmpFile.Name(), signedDocBytes, 0644) | ||
require.NoError(t, err) | ||
outTmpFile.Close() | ||
defer os.Remove(outTmpFile.Name()) | ||
|
||
// Verify | ||
verifyCommand := verifyCommand() | ||
require.NoError(t, runVerifyCmd(verifyCommand, []string{outTmpFile.Name()})) | ||
|
||
// Prepare a file with corrupted signature | ||
signedDoc.Text = "this is an example" | ||
signedDocBytes, err = MarshalJSON(signedDoc) | ||
require.NoError(t, err) | ||
|
||
// Verification fails | ||
outTmpFile, err = ioutil.TempFile("", "") | ||
require.NoError(t, err) | ||
ioutil.WriteFile(outTmpFile.Name(), signedDocBytes, 0644) | ||
require.NoError(t, err) | ||
outTmpFile.Close() | ||
defer os.Remove(outTmpFile.Name()) | ||
|
||
require.Error(t, runVerifyCmd(verifyCommand, []string{outTmpFile.Name()})) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package keys | ||
|
||
import ( | ||
"errors" | ||
"io/ioutil" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
func verifyCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "verify <filename>", | ||
Short: "Verify a plain text's signature", | ||
Long: `Read a document generated with the 'key sign' command and verify the signature. | ||
It exits with 0 if the signature verification succeed; it returns a value different than 0 | ||
if the signature verification fails. | ||
`, | ||
Args: cobra.ExactArgs(1), | ||
RunE: runVerifyCmd, | ||
} | ||
return cmd | ||
} | ||
|
||
func runVerifyCmd(cmd *cobra.Command, args []string) error { | ||
filename := args[0] | ||
|
||
signedDoc, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var doc signedText | ||
if err := UnmarshalJSON(signedDoc, &doc); err != nil { | ||
return err | ||
} | ||
|
||
if doc.Pub.VerifyBytes([]byte(doc.Text), doc.Sig) { | ||
cmd.PrintErrln("signature verified") | ||
return nil | ||
} | ||
return errors.New("bad signature") | ||
} |