From d95b6f50ff25f1170e8259a1de49d278e252221a Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 8 Nov 2023 14:26:25 -0300 Subject: [PATCH] --prompt-sec for getting a secret key from a prompt. --- encode.go | 16 ---------------- event.go | 38 ++++++++++++++++++++++++++++++++++++-- go.mod | 1 + go.sum | 2 ++ helpers.go | 22 +++++++++++++++++++++- 5 files changed, 60 insertions(+), 19 deletions(-) diff --git a/encode.go b/encode.go index 2c337d0..1fc3b08 100644 --- a/encode.go +++ b/encode.go @@ -1,9 +1,7 @@ package main import ( - "encoding/hex" "fmt" - "strings" "github.com/nbd-wtf/go-nostr/nip19" "github.com/urfave/cli/v2" @@ -232,17 +230,3 @@ var encode = &cli.Command{ }, }, } - -func validate32BytesHex(target string) error { - if _, err := hex.DecodeString(target); err != nil { - return fmt.Errorf("target '%s' is not valid hex: %s", target, err) - } - if len(target) != 64 { - return fmt.Errorf("expected '%s' to be 64 characters (32 bytes), got %d", target, len(target)) - } - if strings.ToLower(target) != target { - return fmt.Errorf("expected target to be all lowercase hex. try again with '%s'", strings.ToLower(target)) - } - - return nil -} diff --git a/event.go b/event.go index a060025..bbefc94 100644 --- a/event.go +++ b/event.go @@ -9,8 +9,10 @@ import ( "strings" "time" + "github.com/bgentry/speakeasy" "github.com/mailru/easyjson" "github.com/nbd-wtf/go-nostr" + "github.com/nbd-wtf/go-nostr/nip19" "github.com/nbd-wtf/go-nostr/nson" "github.com/urfave/cli/v2" ) @@ -35,10 +37,14 @@ example: Flags: []cli.Flag{ &cli.StringFlag{ Name: "sec", - Usage: "secret key to sign the event", + Usage: "secret key to sign the event, as hex or nsec", DefaultText: "the key '1'", Value: "0000000000000000000000000000000000000000000000000000000000000001", }, + &cli.BoolFlag{ + Name: "prompt-sec", + Usage: "prompt the user to paste a hex or nsec with which to sign the event", + }, &cli.BoolFlag{ Name: "envelope", Usage: "print the event enveloped in a [\"EVENT\", ...] message ready to be sent to a relay", @@ -90,6 +96,34 @@ example: }, ArgsUsage: "[relay...]", Action: func(c *cli.Context) error { + // gather the secret key first + sec := c.String("sec") + if c.Bool("prompt-sec") { + if isPiped() { + return fmt.Errorf("can't prompt for a secret key when processing data from a pipe, try again without --prompt-sec") + } + var err error + sec, err = speakeasy.FAsk(os.Stderr, "type your secret key as nsec or hex: ") + if err != nil { + return fmt.Errorf("failed to get secret key: %w", err) + } + } + if strings.HasPrefix(sec, "nsec1") { + _, hex, err := nip19.Decode(sec) + if err != nil { + return fmt.Errorf("invalid nsec: %w", err) + } + sec = hex.(string) + } + if len(sec) > 64 { + return fmt.Errorf("invalid secret key: too large") + } + sec = strings.Repeat("0", 64-len(sec)) + sec // left-pad + if err := validate32BytesHex(sec); err != nil { + return fmt.Errorf("invalid secret key") + } + + // then process input and generate events for stdinEvent := range getStdinLinesOrBlank() { evt := nostr.Event{ Tags: make(nostr.Tags, 0, 3), @@ -164,7 +198,7 @@ example: } if evt.Sig == "" || mustRehashAndResign { - if err := evt.Sign(c.String("sec")); err != nil { + if err := evt.Sign(sec); err != nil { return fmt.Errorf("error signing with provided key: %w", err) } } diff --git a/go.mod b/go.mod index e39f434..40ffeb4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 toolchain go1.21.0 require ( + github.com/bgentry/speakeasy v0.1.0 github.com/mailru/easyjson v0.7.7 github.com/nbd-wtf/go-nostr v0.25.3 github.com/nbd-wtf/nostr-sdk v0.0.2 diff --git a/go.sum b/go.sum index 3237f09..8856e51 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= diff --git a/helpers.go b/helpers.go index 6f05666..2497352 100644 --- a/helpers.go +++ b/helpers.go @@ -3,6 +3,7 @@ package main import ( "bufio" "context" + "encoding/hex" "fmt" "net/url" "os" @@ -15,6 +16,11 @@ const ( LINE_PROCESSING_ERROR = iota ) +func isPiped() bool { + stat, _ := os.Stdin.Stat() + return stat.Mode()&os.ModeCharDevice == 0 +} + func getStdinLinesOrBlank() chan string { multi := make(chan string) if hasStdinLines := writeStdinLinesOrNothing(multi); !hasStdinLines { @@ -43,7 +49,7 @@ func getStdinLinesOrFirstArgument(c *cli.Context) chan string { } func writeStdinLinesOrNothing(ch chan string) (hasStdinLines bool) { - if stat, _ := os.Stdin.Stat(); stat.Mode()&os.ModeCharDevice == 0 { + if isPiped() { // piped go func() { scanner := bufio.NewScanner(os.Stdin) @@ -78,6 +84,20 @@ func validateRelayURLs(wsurls []string) error { return nil } +func validate32BytesHex(target string) error { + if _, err := hex.DecodeString(target); err != nil { + return fmt.Errorf("target '%s' is not valid hex: %s", target, err) + } + if len(target) != 64 { + return fmt.Errorf("expected '%s' to be 64 characters (32 bytes), got %d", target, len(target)) + } + if strings.ToLower(target) != target { + return fmt.Errorf("expected target to be all lowercase hex. try again with '%s'", strings.ToLower(target)) + } + + return nil +} + func lineProcessingError(c *cli.Context, msg string, args ...any) { c.Context = context.WithValue(c.Context, LINE_PROCESSING_ERROR, true) fmt.Fprintf(os.Stderr, msg+"\n", args...)