--prompt-sec for getting a secret key from a prompt.

This commit is contained in:
fiatjaf 2023-11-08 14:26:25 -03:00
parent 200e4e61f7
commit d95b6f50ff
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
5 changed files with 60 additions and 19 deletions

View File

@ -1,9 +1,7 @@
package main package main
import ( import (
"encoding/hex"
"fmt" "fmt"
"strings"
"github.com/nbd-wtf/go-nostr/nip19" "github.com/nbd-wtf/go-nostr/nip19"
"github.com/urfave/cli/v2" "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
}

View File

@ -9,8 +9,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/bgentry/speakeasy"
"github.com/mailru/easyjson" "github.com/mailru/easyjson"
"github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/nbd-wtf/go-nostr/nson" "github.com/nbd-wtf/go-nostr/nson"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -35,10 +37,14 @@ example:
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "sec", Name: "sec",
Usage: "secret key to sign the event", Usage: "secret key to sign the event, as hex or nsec",
DefaultText: "the key '1'", DefaultText: "the key '1'",
Value: "0000000000000000000000000000000000000000000000000000000000000001", 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{ &cli.BoolFlag{
Name: "envelope", Name: "envelope",
Usage: "print the event enveloped in a [\"EVENT\", ...] message ready to be sent to a relay", Usage: "print the event enveloped in a [\"EVENT\", ...] message ready to be sent to a relay",
@ -90,6 +96,34 @@ example:
}, },
ArgsUsage: "[relay...]", ArgsUsage: "[relay...]",
Action: func(c *cli.Context) error { 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() { for stdinEvent := range getStdinLinesOrBlank() {
evt := nostr.Event{ evt := nostr.Event{
Tags: make(nostr.Tags, 0, 3), Tags: make(nostr.Tags, 0, 3),
@ -164,7 +198,7 @@ example:
} }
if evt.Sig == "" || mustRehashAndResign { 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) return fmt.Errorf("error signing with provided key: %w", err)
} }
} }

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.21
toolchain go1.21.0 toolchain go1.21.0
require ( require (
github.com/bgentry/speakeasy v0.1.0
github.com/mailru/easyjson v0.7.7 github.com/mailru/easyjson v0.7.7
github.com/nbd-wtf/go-nostr v0.25.3 github.com/nbd-wtf/go-nostr v0.25.3
github.com/nbd-wtf/nostr-sdk v0.0.2 github.com/nbd-wtf/nostr-sdk v0.0.2

2
go.sum
View File

@ -1,4 +1,6 @@
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 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.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.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=

View File

@ -3,6 +3,7 @@ package main
import ( import (
"bufio" "bufio"
"context" "context"
"encoding/hex"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
@ -15,6 +16,11 @@ const (
LINE_PROCESSING_ERROR = iota LINE_PROCESSING_ERROR = iota
) )
func isPiped() bool {
stat, _ := os.Stdin.Stat()
return stat.Mode()&os.ModeCharDevice == 0
}
func getStdinLinesOrBlank() chan string { func getStdinLinesOrBlank() chan string {
multi := make(chan string) multi := make(chan string)
if hasStdinLines := writeStdinLinesOrNothing(multi); !hasStdinLines { if hasStdinLines := writeStdinLinesOrNothing(multi); !hasStdinLines {
@ -43,7 +49,7 @@ func getStdinLinesOrFirstArgument(c *cli.Context) chan string {
} }
func writeStdinLinesOrNothing(ch chan string) (hasStdinLines bool) { func writeStdinLinesOrNothing(ch chan string) (hasStdinLines bool) {
if stat, _ := os.Stdin.Stat(); stat.Mode()&os.ModeCharDevice == 0 { if isPiped() {
// piped // piped
go func() { go func() {
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
@ -78,6 +84,20 @@ func validateRelayURLs(wsurls []string) error {
return nil 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) { func lineProcessingError(c *cli.Context, msg string, args ...any) {
c.Context = context.WithValue(c.Context, LINE_PROCESSING_ERROR, true) c.Context = context.WithValue(c.Context, LINE_PROCESSING_ERROR, true)
fmt.Fprintf(os.Stderr, msg+"\n", args...) fmt.Fprintf(os.Stderr, msg+"\n", args...)