diff --git a/cmd/sing-box/cmd_generate.go b/cmd/sing-box/cmd_generate.go new file mode 100644 index 00000000..3509d975 --- /dev/null +++ b/cmd/sing-box/cmd_generate.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/spf13/cobra" + + "github.com/sagernet/sing-box/cmd/sing-box/internal/generate" +) + +var commandGenerate = &cobra.Command{ + Use: "generate", + Short: "Generate things", +} + +func init() { + commandGenerate.AddCommand(generate.CommandGeneratePSK) + commandGenerate.AddCommand(generate.CommandGenerateUUID) + commandGenerate.AddCommand(generate.CommandGenerateX25519) + mainCommand.AddCommand(commandGenerate) +} diff --git a/cmd/sing-box/internal/generate/psk.go b/cmd/sing-box/internal/generate/psk.go new file mode 100644 index 00000000..720655a2 --- /dev/null +++ b/cmd/sing-box/internal/generate/psk.go @@ -0,0 +1,42 @@ +package generate + +import ( + "crypto/rand" + "encoding/base64" + "encoding/hex" + "os" + + "github.com/sagernet/sing-box/log" + + "github.com/spf13/cobra" +) + +var CommandGeneratePSK = &cobra.Command{ + Use: "psk", + Short: "Generate a random PSK", + Run: func(cmd *cobra.Command, args []string) { + if size > 0 { + encoder := base64.StdEncoding.EncodeToString + if outputHex { + encoder = hex.EncodeToString + } + + psk := make([]byte, size) + _, err := rand.Read(psk) + if err != nil { + log.Fatal(err) + } + + os.Stdout.WriteString(encoder(psk) + "\n") + } else { + cmd.Help() + } + }, +} + +var size int + +func init() { + CommandGeneratePSK.Flags().BoolVarP(&outputHex, "hex", "H", false, "print hex format") + CommandGeneratePSK.Flags().IntVarP(&size, "size", "s", 0, "PSK size") +} diff --git a/cmd/sing-box/internal/generate/shared.go b/cmd/sing-box/internal/generate/shared.go new file mode 100644 index 00000000..293d2f9b --- /dev/null +++ b/cmd/sing-box/internal/generate/shared.go @@ -0,0 +1,6 @@ +package generate + +var ( + outputHex bool + input string +) diff --git a/cmd/sing-box/internal/generate/uuid.go b/cmd/sing-box/internal/generate/uuid.go new file mode 100644 index 00000000..07bf68a8 --- /dev/null +++ b/cmd/sing-box/internal/generate/uuid.go @@ -0,0 +1,36 @@ +package generate + +import ( + "os" + + "github.com/sagernet/sing-box/log" + + "github.com/gofrs/uuid" + "github.com/spf13/cobra" +) + +var CommandGenerateUUID = &cobra.Command{ + Use: "uuid", + Short: "Generate a UUID", + Run: func(cmd *cobra.Command, args []string) { + var ( + newUUID uuid.UUID + err error + ) + + if input == "" { + newUUID, err = uuid.NewV4() + if err != nil { + log.Fatal(err) + } + } else { + newUUID = uuid.NewV5(uuid.Nil, input) + } + + os.Stdout.WriteString(newUUID.String() + "\n") + }, +} + +func init() { + CommandGenerateUUID.Flags().StringVarP(&input, "input", "i", "", "generate UUID v5 from specified string") +} diff --git a/cmd/sing-box/internal/generate/x25519.go b/cmd/sing-box/internal/generate/x25519.go new file mode 100644 index 00000000..a372667b --- /dev/null +++ b/cmd/sing-box/internal/generate/x25519.go @@ -0,0 +1,56 @@ +package generate + +import ( + "crypto/rand" + "encoding/base64" + "encoding/hex" + "os" + + "github.com/sagernet/sing-box/log" + F "github.com/sagernet/sing/common/format" + + "github.com/spf13/cobra" + "golang.org/x/crypto/curve25519" +) + +var CommandGenerateX25519 = &cobra.Command{ + Use: "x25519", + Short: "Generate a X25519 key pair", + Run: func(cmd *cobra.Command, args []string) { + encoder := base64.RawURLEncoding.EncodeToString + if outputHex { + encoder = hex.EncodeToString + } + + var privateKey [curve25519.ScalarSize]byte + if input == "" { + _, err := rand.Read(privateKey[:]) + if err != nil { + log.Fatal(err) + } + } else { + src := []byte(input) + n, _ := base64.RawURLEncoding.Decode(privateKey[:], src) + if n != curve25519.ScalarSize { + n, _ = hex.Decode(privateKey[:], src) + if n != curve25519.ScalarSize { + log.Fatal("invalid input private key") + } + } + } + + publicKey, err := curve25519.X25519(privateKey[:], curve25519.Basepoint) + if err != nil { + log.Fatal(err) + } + + os.Stdout.WriteString(F.ToString( + "Private key: ", encoder(privateKey[:]), + "\nPublic key: ", encoder(publicKey), "\n")) + }, +} + +func init() { + CommandGenerateX25519.Flags().BoolVarP(&outputHex, "hex", "H", false, "print hex format") + CommandGenerateX25519.Flags().StringVarP(&input, "input", "i", "", "generate from specified private key") +}