From 9b2d97f96bd2562641abb66c31ed15030de52907 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 12 Jun 2026 23:25:17 +0200 Subject: [PATCH] cli: add keygen command (#118) * cli: factor out key generation * cli: add keygen command --- cli/init.go | 35 ++++++++++++++--------------------- cli/keygen.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ cli/root.go | 1 + cli/util.go | 23 +++++++++++++++++++++++ 4 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 cli/keygen.go diff --git a/cli/init.go b/cli/init.go index 0d3ddff..f88af31 100644 --- a/cli/init.go +++ b/cli/init.go @@ -8,8 +8,6 @@ import ( "github.com/DataDrake/cli-ng/v2/cmd" "github.com/hyprspace/hyprspace/schema" - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multibase" ) @@ -39,10 +37,7 @@ func InitRun(r *cmd.Root, c *cmd.Sub) { configPath = "/etc/hyprspace/" + ifName + ".json" } - privKey, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 256) - checkErr(err) - - keyBytes, err := crypto.MarshalPrivateKey(privKey) + keyBytes, peerId, err := GenerateKeyPair() checkErr(err) // Setup an initial default config. @@ -72,21 +67,19 @@ func InitRun(r *cmd.Root, c *cmd.Sub) { checkErr(err) fmt.Printf("Initialized new config at %s\n", configPath) - peerId, err := peer.IDFromPrivateKey(privKey) - if err == nil { - fmt.Println("Add this entry to your other peers:") - fmt.Println("{") - name := c.Flags.(*InitFlags).Name - if name == "" { - hostname, err := os.Hostname() - if err == nil { - name = hostname - } + + fmt.Println("Add this entry to your other peers:") + fmt.Println("{") + name := c.Flags.(*InitFlags).Name + if name == "" { + hostname, err := os.Hostname() + if err == nil { + name = hostname } - if name != "" { - fmt.Printf(" \"name\": \"%s\",\n", name) - } - fmt.Printf(" \"id\": \"%s\"\n", peerId) - fmt.Println("}") } + if name != "" { + fmt.Printf(" \"name\": \"%s\",\n", name) + } + fmt.Printf(" \"id\": \"%s\"\n", peerId) + fmt.Println("}") } diff --git a/cli/keygen.go b/cli/keygen.go new file mode 100644 index 0000000..234d0bf --- /dev/null +++ b/cli/keygen.go @@ -0,0 +1,50 @@ +package cli + +import ( + "fmt" + "log" + "os" + + "github.com/DataDrake/cli-ng/v2/cmd" + "github.com/multiformats/go-multibase" +) + +type KeygenFlags struct { + PrivateKeyFile string `long:"private-key-file" desc:"Path to private key file."` + PublicKeyFile string `long:"public-key-file" desc:"Path to public key file. If not specified, prints to stdout."` +} + +// Keygen creates a new keypair and writes each key to a file. +var Keygen = cmd.Sub{ + Name: "keygen", + Short: "Generate a new keypair", + Flags: &KeygenFlags{}, + Run: KeygenRun, +} + +func KeygenRun(r *cmd.Root, c *cmd.Sub) { + flags := c.Flags.(*KeygenFlags) + + if flags.PrivateKeyFile == "" { + log.Fatal("--private-key-file is required") + } + + keyBytes, peerId, err := GenerateKeyPair() + checkErr(err) + + encoded := multibase.MustNewEncoder(multibase.Base58BTC).Encode(keyBytes) + + err = os.WriteFile(flags.PrivateKeyFile, []byte(encoded), 0600) + checkErr(err) + + fmt.Fprintf(os.Stderr, "Private key written to %s\n", flags.PrivateKeyFile) + + if flags.PublicKeyFile != "" { + err = os.WriteFile(flags.PublicKeyFile, []byte(peerId.String()), 0644) + checkErr(err) + + fmt.Fprintf(os.Stderr, "Public key written to %s\n", flags.PublicKeyFile) + } else { + fmt.Println(peerId.String()) + } +} diff --git a/cli/root.go b/cli/root.go index d2a414f..c06a7e2 100644 --- a/cli/root.go +++ b/cli/root.go @@ -30,6 +30,7 @@ func init() { cmd.Register(&cmd.Help) cmd.Register(&Init) + cmd.Register(&Keygen) cmd.Register(&Up) cmd.Register(&Status) cmd.Register(&Peers) diff --git a/cli/util.go b/cli/util.go index 6137837..61c265a 100644 --- a/cli/util.go +++ b/cli/util.go @@ -4,6 +4,8 @@ import ( "fmt" "os" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" "github.com/mattn/go-isatty" ) @@ -16,3 +18,24 @@ func printListF(strings []string, handler func(string) string) { func isTTY() bool { return isatty.IsTerminal(os.Stdout.Fd()) } + +func GenerateKeyPair() ([]byte, peer.ID, error) { + var zeroID peer.ID + + privKey, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 256) + if err != nil { + return nil, zeroID, err + } + + keyBytes, err := crypto.MarshalPrivateKey(privKey) + if err != nil { + return nil, zeroID, err + } + + peerId, err := peer.IDFromPrivateKey(privKey) + if err != nil { + return nil, zeroID, err + } + + return keyBytes, peerId, nil +}