From 798ffec09cc432fa9b05555291c2e101fbb3be3c Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 19 Nov 2020 12:53:17 +0100 Subject: [PATCH] bridge: implement keygen command Tested using `/guardiand keygen /bar --desc foobar`. ghstack-source-id: 9f96ce7c0c7527cde47ecb64099543379fbabf3c Pull Request resolved: https://github.com/certusone/wormhole/pull/91 --- bridge/cmd/guardiand/bridge.go | 20 +++++++++------- bridge/cmd/guardiand/bridgekey.go | 38 +++++++++++++++++++++++++++++++ bridge/cmd/root.go | 1 + 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/bridge/cmd/guardiand/bridge.go b/bridge/cmd/guardiand/bridge.go index 57d47499..f0bc2c46 100644 --- a/bridge/cmd/guardiand/bridge.go +++ b/bridge/cmd/guardiand/bridge.go @@ -122,6 +122,17 @@ func rootLoggerName() string { } } +// lockMemory locks current and future pages in memory to protect secret keys from being swapped out to disk. +// It's possible (and strongly recommended) to deploy Wormhole such that keys are only ever +// stored in memory and never touch the disk. This is a privileged operation and requires CAP_IPC_LOCK. +func lockMemory() { + err := unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) + if err != nil { + fmt.Printf("Failed to lock memory: %v (CAP_IPC_LOCK missing?)\n", err) + os.Exit(1) + } +} + // BridgeCmd represents the bridge command var BridgeCmd = &cobra.Command{ Use: "bridge", @@ -134,14 +145,7 @@ func runBridge(cmd *cobra.Command, args []string) { fmt.Print(devwarning) } - // Lock current and future pages in memory to protect secret keys from being swapped out to disk. - // It's possible (and strongly recommended) to deploy Wormhole such that keys are only ever - // stored in memory and never touch the disk. This is a privileged operation and requires CAP_IPC_LOCK. - err := unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) - if err != nil { - fmt.Printf("Failed to lock memory: %v (CAP_IPC_LOCK missing?)\n", err) - os.Exit(1) - } + lockMemory() // Set up logging. The go-log zap wrapper that libp2p uses is compatible with our // usage of zap in supervisor, which is nice. diff --git a/bridge/cmd/guardiand/bridgekey.go b/bridge/cmd/guardiand/bridgekey.go index 7119b5b4..3df22eaa 100644 --- a/bridge/cmd/guardiand/bridgekey.go +++ b/bridge/cmd/guardiand/bridgekey.go @@ -2,16 +2,50 @@ package guardiand import ( "crypto/ecdsa" + "crypto/rand" + "errors" "fmt" "io/ioutil" + "log" + "os" ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/spf13/cobra" "google.golang.org/protobuf/encoding/prototext" "github.com/certusone/wormhole/bridge/pkg/devnet" nodev1 "github.com/certusone/wormhole/bridge/pkg/proto/node/v1" ) +var keyDescription *string + +func init() { + keyDescription = KeygenCmd.Flags().String("desc", "", "Human-readable key description (optional)") +} + +var KeygenCmd = &cobra.Command{ + Use: "keygen", + Short: "Create guardian key at the specified path", + Run: runKeygen, + Args: cobra.ExactArgs(1), +} + +func runKeygen(cmd *cobra.Command, args []string) { + lockMemory() + + log.Print("Creating new key at ", args[0]) + + gk, err := ecdsa.GenerateKey(ethcrypto.S256(), rand.Reader) + if err != nil { + log.Fatalf("failed to generate key: %v", err) + } + + err = writeGuardianKey(gk, *keyDescription, args[0]) + if err != nil { + log.Fatalf("failed to write key: %v", err) + } +} + // loadGuardianKey loads a serialized guardian key from disk. func loadGuardianKey(filename string) (*ecdsa.PrivateKey, error) { b, err := ioutil.ReadFile(filename) @@ -35,6 +69,10 @@ func loadGuardianKey(filename string) (*ecdsa.PrivateKey, error) { // writeGuardianKey serializes a guardian key and writes it to disk. func writeGuardianKey(key *ecdsa.PrivateKey, description string, filename string) error { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + return errors.New("refusing to override existing key") + } + m := &nodev1.GuardianKey{ Description: description, Data: ethcrypto.FromECDSA(key), diff --git a/bridge/cmd/root.go b/bridge/cmd/root.go index 4c9afd1f..daefb9a7 100644 --- a/bridge/cmd/root.go +++ b/bridge/cmd/root.go @@ -33,6 +33,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.guardiand.yaml)") rootCmd.AddCommand(guardiand.BridgeCmd) + rootCmd.AddCommand(guardiand.KeygenCmd) } // initConfig reads in config file and ENV variables if set.