mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-29 04:31:44 +00:00
Set key algorithm on key creation
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.mykeys
|
76
cmd/README.md
Normal file
76
cmd/README.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Keys CLI
|
||||||
|
|
||||||
|
This is as much an example how to expose cobra/viper, as for a cli itself
|
||||||
|
(I think this code is overkill for what go-keys needs). But please look at
|
||||||
|
the commands, and give feedback and changes.
|
||||||
|
|
||||||
|
`RootCmd` calls some initialization functions (`cobra.OnInitialize` and `RootCmd.PersistentPreRunE`) which serve to connect environmental variables and cobra flags, as well as load the config file. It also validates the flags registered on root and creates the cryptomanager, which will be used by all subcommands.
|
||||||
|
|
||||||
|
## Help info
|
||||||
|
|
||||||
|
```
|
||||||
|
# keys help
|
||||||
|
Keys allows you to manage your local keystore for tendermint.
|
||||||
|
|
||||||
|
These keys may be in any format supported by go-crypto and can be
|
||||||
|
used by light-clients, full nodes, or any other application that
|
||||||
|
needs to sign with a private key.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
keys [command]
|
||||||
|
|
||||||
|
Available Commands:
|
||||||
|
list List all keys
|
||||||
|
new Create a new public/private key pair
|
||||||
|
serve Run the key manager as an http server
|
||||||
|
update Change the password for a private key
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
--keydir string Directory to store private keys (subdir of root) (default "keys")
|
||||||
|
-o, --output string Output format (text|json) (default "text")
|
||||||
|
-r, --root string root directory for config and data (default "/Users/ethan/.tlc")
|
||||||
|
|
||||||
|
Use "keys [command] --help" for more information about a command.```
|
||||||
|
|
||||||
|
## Getting the config file
|
||||||
|
|
||||||
|
The first step is to load in root, by checking the following in order:
|
||||||
|
|
||||||
|
* -r, --root command line flag
|
||||||
|
* TM_ROOT environmental variable
|
||||||
|
* default ($HOME/.tlc evaluated at runtime)
|
||||||
|
|
||||||
|
Once the `rootDir` is established, the script looks for a config file named `keys.{json,toml,yaml,hcl}` in that directory and parses it. These values will provide defaults for flags of the same name.
|
||||||
|
|
||||||
|
## Getting/Setting variables
|
||||||
|
|
||||||
|
When we want to get the value of a user-defined variable (eg. `output`), we can call `viper.GetString("output")`, which will do the following checks, until it finds a match:
|
||||||
|
|
||||||
|
* Is `--output` command line flag present?
|
||||||
|
* Is `TM_OUTPUT` environmental variable set?
|
||||||
|
* Was a config file found and does it have an `output` variable?
|
||||||
|
* Is there a default set on the command line flag?
|
||||||
|
|
||||||
|
If no variable is set and there was no default, we get back "".
|
||||||
|
|
||||||
|
This setup allows us to have powerful command line flags, but use env variables or config files (local or 12-factor style) to avoid passing these arguments every time.
|
||||||
|
|
||||||
|
## Nesting structures
|
||||||
|
|
||||||
|
Sometimes we don't just need key-value pairs, but actually a multi-level config file, like
|
||||||
|
|
||||||
|
```
|
||||||
|
[mail]
|
||||||
|
from = "no-reply@example.com"
|
||||||
|
server = "mail.example.com"
|
||||||
|
port = 567
|
||||||
|
password = "XXXXXX"
|
||||||
|
```
|
||||||
|
|
||||||
|
This CLI is too simple to warant such a structure, but I think eg. tendermint could benefit from such an approach. Here are some pointers:
|
||||||
|
|
||||||
|
* [Accessing nested keys from config files](https://github.com/spf13/viper#accessing-nested-keys)
|
||||||
|
* [Overriding nested values with envvars](https://www.netlify.com/blog/2016/09/06/creating-a-microservice-boilerplate-in-go/#nested-config-values) - the mentioned outstanding PR is already merged into master!
|
||||||
|
* Overriding nested values with cli flags? (use `--log_config.level=info` ??)
|
||||||
|
|
||||||
|
I'd love to see an example of this fully worked out in a more complex CLI.
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// newCmd represents the new command
|
// newCmd represents the new command
|
||||||
@ -32,6 +33,7 @@ passed as a command line argument for security.`,
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCmd.AddCommand(newCmd)
|
RootCmd.AddCommand(newCmd)
|
||||||
|
newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPassword(cmd *cobra.Command, args []string) {
|
func newPassword(cmd *cobra.Command, args []string) {
|
||||||
@ -40,6 +42,7 @@ func newPassword(cmd *cobra.Command, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
algo := viper.GetString("type")
|
||||||
|
|
||||||
pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:")
|
pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -47,7 +50,7 @@ func newPassword(cmd *cobra.Command, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := manager.Create(name, pass)
|
info, err := manager.Create(name, pass, algo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return
|
return
|
||||||
|
@ -58,9 +58,9 @@ func Execute() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cobra.OnInitialize(initEnv)
|
cobra.OnInitialize(initEnv)
|
||||||
RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data (default is TM_ROOT or $HOME/.tlc)")
|
RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data")
|
||||||
RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)")
|
RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)")
|
||||||
RootCmd.PersistentFlags().StringP("keydir", "", "keys", "Directory to store private keys (subdir of root)")
|
RootCmd.PersistentFlags().String("keydir", "keys", "Directory to store private keys (subdir of root)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// initEnv sets to use ENV variables if set.
|
// initEnv sets to use ENV variables if set.
|
||||||
@ -107,7 +107,6 @@ func validateFlags(cmd *cobra.Command) error {
|
|||||||
}
|
}
|
||||||
// and construct the key manager
|
// and construct the key manager
|
||||||
manager = cryptostore.New(
|
manager = cryptostore.New(
|
||||||
cryptostore.GenEd25519, // TODO - cli switch???
|
|
||||||
cryptostore.SecretBox,
|
cryptostore.SecretBox,
|
||||||
filestorage.New(keyDir),
|
filestorage.New(keyDir),
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package cryptostore
|
package cryptostore
|
||||||
|
|
||||||
import crypto "github.com/tendermint/go-crypto"
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// GenEd25519 produces Ed25519 private keys
|
// GenEd25519 produces Ed25519 private keys
|
||||||
@ -28,3 +31,14 @@ func genEd25519() crypto.PrivKey {
|
|||||||
func genSecp256() crypto.PrivKey {
|
func genSecp256() crypto.PrivKey {
|
||||||
return crypto.GenPrivKeySecp256k1()
|
return crypto.GenPrivKeySecp256k1()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGenerator(algo string) (Generator, error) {
|
||||||
|
switch algo {
|
||||||
|
case crypto.NameEd25519:
|
||||||
|
return GenEd25519, nil
|
||||||
|
case crypto.NameSecp256k1:
|
||||||
|
return GenSecp256k1, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("Cannot generate keys for algorithm: %s", algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,13 +5,11 @@ import keys "github.com/tendermint/go-keys"
|
|||||||
// Manager combines encyption and storage implementation to provide
|
// Manager combines encyption and storage implementation to provide
|
||||||
// a full-featured key manager
|
// a full-featured key manager
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
gen Generator
|
es encryptedStorage
|
||||||
es encryptedStorage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(gen Generator, coder Encoder, store keys.Storage) Manager {
|
func New(coder Encoder, store keys.Storage) Manager {
|
||||||
return Manager{
|
return Manager{
|
||||||
gen: gen,
|
|
||||||
es: encryptedStorage{
|
es: encryptedStorage{
|
||||||
coder: coder,
|
coder: coder,
|
||||||
store: store,
|
store: store,
|
||||||
@ -31,9 +29,16 @@ func (s Manager) assertKeyManager() keys.Manager {
|
|||||||
|
|
||||||
// Create adds a new key to the storage engine, returning error if
|
// Create adds a new key to the storage engine, returning error if
|
||||||
// another key already stored under this name
|
// another key already stored under this name
|
||||||
func (s Manager) Create(name, passphrase string) (keys.Info, error) {
|
//
|
||||||
key := s.gen.Generate()
|
// algo must be a supported go-crypto algorithm:
|
||||||
err := s.es.Put(name, passphrase, key)
|
//
|
||||||
|
func (s Manager) Create(name, passphrase, algo string) (keys.Info, error) {
|
||||||
|
gen, err := getGenerator(algo)
|
||||||
|
if err != nil {
|
||||||
|
return keys.Info{}, err
|
||||||
|
}
|
||||||
|
key := gen.Generate()
|
||||||
|
err = s.es.Put(name, passphrase, key)
|
||||||
return info(name, key), err
|
return info(name, key), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-keys/cryptostore"
|
"github.com/tendermint/go-keys/cryptostore"
|
||||||
"github.com/tendermint/go-keys/storage/memstorage"
|
"github.com/tendermint/go-keys/storage/memstorage"
|
||||||
)
|
)
|
||||||
@ -15,11 +16,11 @@ func TestKeyManagement(t *testing.T) {
|
|||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := cryptostore.New(
|
||||||
cryptostore.GenSecp256k1,
|
|
||||||
cryptostore.SecretBox,
|
cryptostore.SecretBox,
|
||||||
memstorage.New(),
|
memstorage.New(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
algo := crypto.NameEd25519
|
||||||
n1, n2, n3 := "personal", "business", "other"
|
n1, n2, n3 := "personal", "business", "other"
|
||||||
p1, p2 := "1234", "really-secure!@#$"
|
p1, p2 := "1234", "really-secure!@#$"
|
||||||
|
|
||||||
@ -31,10 +32,10 @@ func TestKeyManagement(t *testing.T) {
|
|||||||
// create some keys
|
// create some keys
|
||||||
_, err = cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
i, err := cstore.Create(n1, p1)
|
i, err := cstore.Create(n1, p1, algo)
|
||||||
require.Equal(n1, i.Name)
|
require.Equal(n1, i.Name)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
_, err = cstore.Create(n2, p2)
|
_, err = cstore.Create(n2, p2, algo)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
|
|
||||||
// we can get these keys
|
// we can get these keys
|
||||||
@ -151,16 +152,16 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := cryptostore.New(
|
||||||
cryptostore.GenSecp256k1,
|
|
||||||
cryptostore.SecretBox,
|
cryptostore.SecretBox,
|
||||||
memstorage.New(),
|
memstorage.New(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
algo := crypto.NameSecp256k1
|
||||||
n1, n2 := "old-name", "new name"
|
n1, n2 := "old-name", "new name"
|
||||||
p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$"
|
p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$"
|
||||||
|
|
||||||
// make sure key works with initial password
|
// make sure key works with initial password
|
||||||
_, err := cstore.Create(n1, p1)
|
_, err := cstore.Create(n1, p1, algo)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
assertPassword(assert, cstore, n1, p1, p2)
|
assertPassword(assert, cstore, n1, p1, p2)
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ type Signer interface {
|
|||||||
|
|
||||||
// Manager allows simple CRUD on a keystore, as an aid to signing
|
// Manager allows simple CRUD on a keystore, as an aid to signing
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
Create(name, passphrase string) (Info, error)
|
Create(name, passphrase, algo string) (Info, error)
|
||||||
List() (Infos, error)
|
List() (Infos, error)
|
||||||
Get(name string) (Info, error)
|
Get(name string) (Info, error)
|
||||||
Update(name, oldpass, newpass string) error
|
Update(name, oldpass, newpass string) error
|
||||||
|
Reference in New Issue
Block a user