cmd/clef, signer: support removing stored keys (delpw + rules)

This commit is contained in:
Péter Szilágyi 2019-07-02 12:11:26 +03:00
parent 793db11483
commit 31c2781d49
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
4 changed files with 82 additions and 12 deletions

View file

@ -93,7 +93,7 @@ var (
chainIdFlag = cli.Int64Flag{ chainIdFlag = cli.Int64Flag{
Name: "chainid", Name: "chainid",
Value: params.MainnetChainConfig.ChainID.Int64(), Value: params.MainnetChainConfig.ChainID.Int64(),
Usage: "Chain id to use for signing (1=mainnet, 3=ropsten, 4=rinkeby, 5=Goerli)", Usage: "Chain id to use for signing (1=mainnet, 3=Ropsten, 4=Rinkeby, 5=Goerli)",
} }
rpcPortFlag = cli.IntFlag{ rpcPortFlag = cli.IntFlag{
Name: "rpcport", Name: "rpcport",
@ -116,8 +116,7 @@ var (
} }
ruleFlag = cli.StringFlag{ ruleFlag = cli.StringFlag{
Name: "rules", Name: "rules",
Usage: "Enable rule-engine", Usage: "Path to the rule file to auto-authorize requests with",
Value: "",
} }
stdiouiFlag = cli.BoolFlag{ stdiouiFlag = cli.BoolFlag{
Name: "stdio-ui", Name: "stdio-ui",
@ -160,7 +159,6 @@ incoming requests.
Whenever you make an edit to the rule file, you need to use attestation to tell Whenever you make an edit to the rule file, you need to use attestation to tell
Clef that the file is 'safe' to execute.`, Clef that the file is 'safe' to execute.`,
} }
setCredentialCommand = cli.Command{ setCredentialCommand = cli.Command{
Action: utils.MigrateFlags(setCredential), Action: utils.MigrateFlags(setCredential),
Name: "setpw", Name: "setpw",
@ -173,6 +171,19 @@ Clef that the file is 'safe' to execute.`,
}, },
Description: ` Description: `
The setpw command stores a password for a given address (keyfile). The setpw command stores a password for a given address (keyfile).
`}
delCredentialCommand = cli.Command{
Action: utils.MigrateFlags(removeCredential),
Name: "delpw",
Usage: "Remove a credential for a keystore file",
ArgsUsage: "<address>",
Flags: []cli.Flag{
logLevelFlag,
configdirFlag,
signerSecretFlag,
},
Description: `
The delpw command removes a password for a given address (keyfile).
`} `}
gendocCommand = cli.Command{ gendocCommand = cli.Command{
Action: GenDoc, Action: GenDoc,
@ -209,9 +220,9 @@ func init() {
advancedMode, advancedMode,
} }
app.Action = signer app.Action = signer
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, gendocCommand} app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
} }
func main() { func main() {
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
@ -317,7 +328,6 @@ func setCredential(ctx *cli.Context) error {
if err := initialize(ctx); err != nil { if err := initialize(ctx); err != nil {
return err return err
} }
addr := ctx.Args().First() addr := ctx.Args().First()
if !common.IsHexAddress(addr) { if !common.IsHexAddress(addr) {
utils.Fatalf("Invalid address specified: %s", addr) utils.Fatalf("Invalid address specified: %s", addr)
@ -334,10 +344,38 @@ func setCredential(ctx *cli.Context) error {
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
// Initialize the encrypted storages
pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
pwStorage.Put(address.Hex(), password) pwStorage.Put(address.Hex(), password)
log.Info("Credential store updated", "key", address)
log.Info("Credential store updated", "set", address)
return nil
}
func removeCredential(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an address to be passed as an argument")
}
if err := initialize(ctx); err != nil {
return err
}
addr := ctx.Args().First()
if !common.IsHexAddress(addr) {
utils.Fatalf("Invalid address specified: %s", addr)
}
address := common.HexToAddress(addr)
stretchedKey, err := readMasterKey(ctx, nil)
if err != nil {
utils.Fatalf(err.Error())
}
configDir := ctx.GlobalString(configdirFlag.Name)
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
pwStorage.Del(address.Hex())
log.Info("Credential store updated", "unset", address)
return nil return nil
} }
@ -359,6 +397,10 @@ func initialize(c *cli.Context) error {
} }
func signer(c *cli.Context) error { func signer(c *cli.Context) error {
// If we have some unrecognized command, bail out
if args := c.Args(); len(args) > 0 {
return fmt.Errorf("invalid command: %q", args[0])
}
if err := initialize(c); err != nil { if err := initialize(c); err != nil {
return err return err
} }

View file

@ -83,7 +83,12 @@ func (r *rulesetUI) execute(jsfunc string, jsarg interface{}) (otto.Value, error
vm.Set("storage", struct{}{}) vm.Set("storage", struct{}{})
storageObj, _ := vm.Get("storage") storageObj, _ := vm.Get("storage")
storageObj.Object().Set("put", func(call otto.FunctionCall) otto.Value { storageObj.Object().Set("put", func(call otto.FunctionCall) otto.Value {
r.storage.Put(call.Argument(0).String(), call.Argument(1).String()) key, val := call.Argument(0).String(), call.Argument(1).String()
if val == "" {
r.storage.Del(key)
} else {
r.storage.Put(key, val)
}
return otto.NullValue() return otto.NullValue()
}) })
storageObj.Object().Set("get", func(call otto.FunctionCall) otto.Value { storageObj.Object().Set("get", func(call otto.FunctionCall) otto.Value {

View file

@ -53,7 +53,7 @@ func NewAESEncryptedStorage(filename string, key []byte) *AESEncryptedStorage {
} }
} }
// Put stores a value by key. 0-length keys results in no-op // Put stores a value by key. 0-length keys results in noop.
func (s *AESEncryptedStorage) Put(key, value string) { func (s *AESEncryptedStorage) Put(key, value string) {
if len(key) == 0 { if len(key) == 0 {
return return
@ -99,6 +99,19 @@ func (s *AESEncryptedStorage) Get(key string) (string, error) {
return string(entry), nil return string(entry), nil
} }
// Del removes a key-value pair. If the key doesn't exist, the method is a noop.
func (s *AESEncryptedStorage) Del(key string) {
data, err := s.readEncryptedStorage()
if err != nil {
log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
return
}
delete(data, key)
if err = s.writeEncryptedStorage(data); err != nil {
log.Warn("Failed to write entry", "err", err)
}
}
// readEncryptedStorage reads the file with encrypted creds // readEncryptedStorage reads the file with encrypted creds
func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) { func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) {
creds := make(map[string]storedCredential) creds := make(map[string]storedCredential)

View file

@ -28,12 +28,15 @@ var (
) )
type Storage interface { type Storage interface {
// Put stores a value by key. 0-length keys results in no-op // Put stores a value by key. 0-length keys results in noop.
Put(key, value string) Put(key, value string)
// Get returns the previously stored value, or an error if the key is 0-length // Get returns the previously stored value, or an error if the key is 0-length
// or unknown. // or unknown.
Get(key string) (string, error) Get(key string) (string, error)
// Del removes a key-value pair. If the key doesn't exist, the method is a noop.
Del(key string)
} }
// EphemeralStorage is an in-memory storage that does // EphemeralStorage is an in-memory storage that does
@ -43,6 +46,7 @@ type EphemeralStorage struct {
namespace string namespace string
} }
// Put stores a value by key. 0-length keys results in noop.
func (s *EphemeralStorage) Put(key, value string) { func (s *EphemeralStorage) Put(key, value string) {
if len(key) == 0 { if len(key) == 0 {
return return
@ -62,6 +66,11 @@ func (s *EphemeralStorage) Get(key string) (string, error) {
return "", ErrNotFound return "", ErrNotFound
} }
// Del removes a key-value pair. If the key doesn't exist, the method is a noop.
func (s *EphemeralStorage) Del(key string) {
delete(s.data, key)
}
func NewEphemeralStorage() Storage { func NewEphemeralStorage() Storage {
s := &EphemeralStorage{ s := &EphemeralStorage{
data: make(map[string]string), data: make(map[string]string),
@ -73,6 +82,7 @@ func NewEphemeralStorage() Storage {
type NoStorage struct{} type NoStorage struct{}
func (s *NoStorage) Put(key, value string) {} func (s *NoStorage) Put(key, value string) {}
func (s *NoStorage) Del(key string) {}
func (s *NoStorage) Get(key string) (string, error) { func (s *NoStorage) Get(key string) (string, error) {
return "", errors.New("I forgot") return "", errors.New("I forgot")
} }