Base features done
This commit is contained in:
parent
12f205fae4
commit
08d20361bf
54
cmd/cnf.go
54
cmd/cnf.go
|
@ -21,8 +21,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.olznet.de/OlzNet/qmkenc"
|
"gitea.olznet.de/OlzNet/qmkenc"
|
||||||
|
"gitea.olznet.de/OlzNet/slog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cnfSetFlags *pflag.FlagSet
|
var cnfSetFlags *pflag.FlagSet
|
||||||
|
@ -45,25 +48,27 @@ func cfgFormat(cfg *qmkenc.QCfg) string {
|
||||||
// cnfCmd represents the cnf command
|
// cnfCmd represents the cnf command
|
||||||
var cnfCmd = &cobra.Command{
|
var cnfCmd = &cobra.Command{
|
||||||
Use: "cnf [get|set] [flags]",
|
Use: "cnf [get|set] [flags]",
|
||||||
Short: "A brief description of your command",
|
Short: "Manage configuration for encryption enabled qmk devices",
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
Long: `Via get the actual configuration can be retrieved. This
|
||||||
and usage of using your command. For example:
|
can be used for monitoring apps to directly show every aspect of the
|
||||||
|
configuration for the device.
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
Set can be used to change the configuration of the device.
|
||||||
This application is a tool to generate the needed files
|
If enabling paranoia mode of a device be aware that this
|
||||||
to quickly create a Cobra application.`,
|
cannot be undone without loosing your keys.`,
|
||||||
ValidArgs: []string{"get", "set"},
|
ValidArgs: []string{"get", "set"},
|
||||||
Args: cobra.ExactValidArgs(1),
|
Args: cobra.ExactValidArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "get":
|
case "get":
|
||||||
cfg, err := qmkenc.QCmdGetCfg(qdev)
|
cfg, err := qmkenc.QCmdGetCfg(qdev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if subViper.GetBool("format") {
|
if subViper.GetBool("format") {
|
||||||
fmt.Println(cfgFormat(cfg))
|
fmt.Println(cfgFormat(cfg))
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
if subViper.GetBool("max_error") {
|
if subViper.GetBool("max_error") {
|
||||||
fmt.Printf("%d\n", cfg.MaxError())
|
fmt.Printf("%d\n", cfg.MaxError())
|
||||||
|
@ -88,35 +93,52 @@ to quickly create a Cobra application.`,
|
||||||
f := cnfSetFlags.Lookup("max_error")
|
f := cnfSetFlags.Lookup("max_error")
|
||||||
if f != nil && f.Changed {
|
if f != nil && f.Changed {
|
||||||
if err := qmkenc.QCmdSetCfgMaxError(qdev, uint8(subViper.GetInt("max_error"))); err != nil {
|
if err := qmkenc.QCmdSetCfgMaxError(qdev, uint8(subViper.GetInt("max_error"))); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f = cnfSetFlags.Lookup("paranoia_mode")
|
f = cnfSetFlags.Lookup("paranoia_mode")
|
||||||
if f != nil && f.Changed {
|
if f != nil && f.Changed {
|
||||||
if subViper.GetBool("paranoia_mode") == false {
|
if subViper.GetBool("paranoia_mode") == false {
|
||||||
return fmt.Errorf("Paranoia Mode can't be unset -> YOU'VE BEEN WARNED !!!!")
|
slog.LOG_ERRORLN("Paranoia Mode can't be unset -> YOU'VE BEEN WARNED !!!!")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "Paranoia mode cannot be unset. Make sure you HAVE STORED your keys. Are you sure to procceed",
|
||||||
|
Items: []string{"Yes", "No"},
|
||||||
|
}
|
||||||
|
_, result, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_ERRORFLN("Prompt failed %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if result == "Yes" {
|
||||||
|
slog.LOG_DEBUGLN("Enabling paranoia mode")
|
||||||
if err := qmkenc.QCmdSetCfgParanoia(qdev); err != nil {
|
if err := qmkenc.QCmdSetCfgParanoia(qdev); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f = cnfSetFlags.Lookup("secure_mode")
|
f = cnfSetFlags.Lookup("secure_mode")
|
||||||
if f != nil && f.Changed {
|
if f != nil && f.Changed {
|
||||||
if err := qmkenc.QCmdSetCfgSecure(qdev, subViper.GetBool("secure_mode")); err != nil {
|
if err := qmkenc.QCmdSetCfgSecure(qdev, subViper.GetBool("secure_mode")); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f = cnfSetFlags.Lookup("timeout_min")
|
f = cnfSetFlags.Lookup("timeout_min")
|
||||||
if f != nil && f.Changed {
|
if f != nil && f.Changed {
|
||||||
if err := qmkenc.QCmdSetCfgTimeout(qdev, uint8(subViper.GetInt("timeout_min"))); err != nil {
|
if err := qmkenc.QCmdSetCfgTimeout(qdev, uint8(subViper.GetInt("timeout_min"))); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Invalid command: %s", args[0])
|
slog.LOG_ERRORFLN("Invalid command: %s", args[0])
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
57
cmd/crypt.go
57
cmd/crypt.go
|
@ -23,13 +23,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"golang.design/x/clipboard"
|
"golang.design/x/clipboard"
|
||||||
|
|
||||||
"gitea.olznet.de/OlzNet/qmkenc"
|
"gitea.olznet.de/OlzNet/qmkenc"
|
||||||
|
"gitea.olznet.de/OlzNet/slog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readIn() (in []byte, err error) {
|
func cryptReadIn() (in []byte, err error) {
|
||||||
if subViper.GetBool("cin") {
|
if subViper.GetBool("cin") {
|
||||||
in = clipboard.Read(clipboard.FmtText)
|
in = clipboard.Read(clipboard.FmtText)
|
||||||
} else {
|
} else {
|
||||||
|
@ -68,61 +68,58 @@ func writeOut(out []byte) (c <-chan struct{}, err error) {
|
||||||
// cryptCmd represents the crypt command
|
// cryptCmd represents the crypt command
|
||||||
var cryptCmd = &cobra.Command{
|
var cryptCmd = &cobra.Command{
|
||||||
Use: "crypt [encrypt|decrypt] [flags]",
|
Use: "crypt [encrypt|decrypt] [flags]",
|
||||||
Short: "A brief description of your command",
|
Short: "Encrypt or decrypt data by using the qmk device",
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
Long: `The data can be provided via clipboard or stdin and
|
||||||
and usage of using your command. For example:
|
by using base64 encoding.
|
||||||
|
The default will use clipboard without base64 encoding.`,
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
ValidArgs: []string{"encrypt", "decrypt"},
|
ValidArgs: []string{"encrypt", "decrypt"},
|
||||||
Args: cobra.ExactValidArgs(1),
|
Args: cobra.ExactValidArgs(1),
|
||||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if subViper.GetBool("cin") || subViper.GetBool("cout") {
|
if subViper.GetBool("cin") || subViper.GetBool("cout") {
|
||||||
if err := clipboard.Init(); err != nil {
|
if err := clipboard.Init(); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
in, err := cryptReadIn()
|
||||||
},
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
in, err := readIn()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
var out []byte
|
||||||
|
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "encrypt":
|
case "encrypt":
|
||||||
if err = qmkenc.QCmdEncrypt(qdev, in); err != nil {
|
if out, err = qmkenc.QCmdEncrypt(qdev, in); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "decrypt":
|
case "decrypt":
|
||||||
if err = qmkenc.QCmdDecrypt(qdev, in); err != nil {
|
if out, err = qmkenc.QCmdDecrypt(qdev, in); err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Invalid command: %s", args[0])
|
slog.LOG_ERRORLN(fmt.Errorf("Invalid command: %s", args[0]))
|
||||||
}
|
return
|
||||||
out, err := qmkenc.QCmdGetBuffer(qdev)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := writeOut(out)
|
c, err := writeOut(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-c:
|
case <-c:
|
||||||
return nil
|
return
|
||||||
case <-time.After(time.Second * time.Duration(viper.GetInt("timeout"))):
|
case <-time.After(time.Second * time.Duration(subViper.GetInt("timeout"))):
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
39
cmd/dev.go
39
cmd/dev.go
|
@ -19,31 +19,42 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"gitea.olznet.de/OlzNet/qmkenc"
|
"gitea.olznet.de/OlzNet/qmkenc"
|
||||||
|
"gitea.olznet.de/OlzNet/slog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// devCmd represents the dev command
|
// devCmd represents the dev command
|
||||||
var devCmd = &cobra.Command{
|
var devCmd = &cobra.Command{
|
||||||
Use: "dev [unlock|lock|initialize|reset] [flags]",
|
Use: "dev [unlock|lock|initialize|reset|mode] [flags]",
|
||||||
Short: "A brief description of your command",
|
Short: "Device commands used to manage basic aspects",
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
Long: `This command is used to lock, unlock, initialize, reset
|
||||||
and usage of using your command. For example:
|
or retrieving the run mode of the device.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
if len(args) == 0 {
|
||||||
This application is a tool to generate the needed files
|
slog.LOG_ERRORLN("No command")
|
||||||
to quickly create a Cobra application.`,
|
return
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
}
|
||||||
|
var err error
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "unlock":
|
case "unlock":
|
||||||
return qmkenc.QCmdUnlock(qdev)
|
err = qmkenc.QCmdUnlock(qdev)
|
||||||
case "lock":
|
case "lock":
|
||||||
return qmkenc.QCmdLock(qdev)
|
err = qmkenc.QCmdLock(qdev)
|
||||||
case "initialize":
|
case "initialize":
|
||||||
return qmkenc.QCmdInitialize(qdev)
|
err = qmkenc.QCmdInitialize(qdev)
|
||||||
case "reset":
|
case "reset":
|
||||||
return qmkenc.QCmdReset(qdev)
|
err = qmkenc.QCmdReset(qdev)
|
||||||
|
case "mode":
|
||||||
|
m, sm, err := qmkenc.QCmdGetMode(qdev)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("M: %s, SM: %s\n", m, sm)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Invalid command: %s", args[0])
|
slog.LOG_ERRORFLN("Invalid command: %s", args[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
keepassxc_browser "gitea.olznet.de/OlzNet/golang-keepassxc-browser"
|
||||||
|
"gitea.olznet.de/OlzNet/qmkenc"
|
||||||
|
"gitea.olznet.de/OlzNet/slog"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reqM struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reqM) ModifyReq(c *keepassxc_browser.ConnMsg) error {
|
||||||
|
slog.LOG_DEBUGF("Req Msg: %v\n", c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reqM) ModifyRes(c *keepassxc_browser.ConnMsg) error {
|
||||||
|
slog.LOG_DEBUGF("Res Msg: %v\n", c)
|
||||||
|
switch c.ActionName {
|
||||||
|
case "get-logins":
|
||||||
|
resi := c.GetData().(*keepassxc_browser.MsgGetLogins)
|
||||||
|
for i, e := range resi.Entries {
|
||||||
|
slog.LOG_DEBUGF("e: %v\n", e)
|
||||||
|
newStringFields := []map[string]string{}
|
||||||
|
for _, f := range e.StringFields {
|
||||||
|
v, ok := f["KPH: qmkenc"]
|
||||||
|
if ok {
|
||||||
|
slog.LOG_DEBUGF("OK: %s\n", v)
|
||||||
|
slog.LOG_DEBUGF("OK e: %s\n", e)
|
||||||
|
switch v {
|
||||||
|
case "login":
|
||||||
|
if out, err := qmkenc.QCmdDecrypt(qdev, []byte(e.Login)); err != nil {
|
||||||
|
resi.Entries[i].Login = ""
|
||||||
|
} else {
|
||||||
|
resi.Entries[i].Login = string(out)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "password":
|
||||||
|
slog.LOG_DEBUGF("OK pass e: %s\n", e)
|
||||||
|
slog.LOG_DEBUGF("Old pass: %v\n", e.Password)
|
||||||
|
slog.LOG_DEBUGF("Old login: %v\n", e.Login)
|
||||||
|
pass, err := base64.StdEncoding.DecodeString(e.Password)
|
||||||
|
if err != nil {
|
||||||
|
resi.Entries[i].Password = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if out, err := qmkenc.QCmdDecrypt(qdev, []byte(pass)); err != nil {
|
||||||
|
slog.LOG_DEBUGLN(err)
|
||||||
|
resi.Entries[i].Password = ""
|
||||||
|
} else {
|
||||||
|
slog.LOG_DEBUGF("Old pass: %s\n", e.Password)
|
||||||
|
slog.LOG_DEBUGF("New pass: %s\n", out)
|
||||||
|
resi.Entries[i].Password = string(out)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newStringFields = append(newStringFields, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resi.Entries[i].StringFields = newStringFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// keepassxcproxyCmd represents the keepassxcproxy command
|
||||||
|
var keepassxcproxyCmd = &cobra.Command{
|
||||||
|
Use: "keepassxcproxy",
|
||||||
|
Short: "Proxy to interact with keepassxc via man in the middle",
|
||||||
|
Long: `This proxy will decrypt entries that are stored encrypted
|
||||||
|
and base64 encoded in login or password fields of keepassxc.
|
||||||
|
|
||||||
|
It will check for additional attribute "KPH: qemkenc" and pass the values
|
||||||
|
which can be either "password" or "login" for the password or the username
|
||||||
|
(login field) and decrypt them before providing to the requesting client.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.LOG_ERRORF("Starting proxy\n")
|
||||||
|
kpProxy, err := keepassxc_browser.NewKpXcMitm(new(reqM))
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_DEBUGF("error: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.LOG_DEBUGF("Creating server\n")
|
||||||
|
server := keepassxc_browser.NewServer(kpProxy, &keepassxc_browser.StdinoutConnection{})
|
||||||
|
slog.LOG_DEBUGF("Starting run\n")
|
||||||
|
if err = server.Run(); err != nil {
|
||||||
|
slog.LOG_DEBUGF("error: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.LOG_DEBUGF("Proxy end\n")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(keepassxcproxyCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// keepassxcproxyCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// keepassxcproxyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
112
cmd/keys.go
112
cmd/keys.go
|
@ -16,36 +16,105 @@ limitations under the License.
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gitea.olznet.de/OlzNet/eqmk/eqmkcommon"
|
||||||
"gitea.olznet.de/OlzNet/qmkenc"
|
"gitea.olznet.de/OlzNet/qmkenc"
|
||||||
|
"gitea.olznet.de/OlzNet/slog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"golang.design/x/clipboard"
|
||||||
)
|
)
|
||||||
|
|
||||||
var keysSetFlags *pflag.FlagSet
|
var keysSetFlags *pflag.FlagSet
|
||||||
var keysGetFlags *pflag.FlagSet
|
var keysGetFlags *pflag.FlagSet
|
||||||
|
|
||||||
|
func keysReadIn() (in []byte, err error) {
|
||||||
|
if subViper.GetBool("cin") {
|
||||||
|
in = clipboard.Read(clipboard.FmtText)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Enter the key as hex encoded string without spaces (ae. adeeff1a...):")
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
scanner.Scan()
|
||||||
|
err = scanner.Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
in = scanner.Bytes()
|
||||||
|
slog.LOG_DEBUGF("input: %s\n", in)
|
||||||
|
}
|
||||||
|
if _in, err := hex.DecodeString(string(in)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
in = _in
|
||||||
|
}
|
||||||
|
if len(in) != 32 {
|
||||||
|
slog.LOG_DEBUGF("decoded: %v\n", in)
|
||||||
|
return nil, fmt.Errorf("Incorrect key len. Got: %d, expected 32", len(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
return in, err
|
||||||
|
}
|
||||||
|
|
||||||
// keysCmd represents the keys command
|
// keysCmd represents the keys command
|
||||||
var keysCmd = &cobra.Command{
|
var keysCmd = &cobra.Command{
|
||||||
Use: "keys [get|set] [flags]",
|
Use: "keys [get|set] [flags]",
|
||||||
Short: "A brief description of your command",
|
Short: "Managing cryptographic keys of the qmk device",
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
Long: `Retrieving the keys via get to store them in a safe place.
|
||||||
and usage of using your command. For example:
|
You can use human readable format to easily write them to a paper.
|
||||||
|
Also it is possible to set the keys (only if paranoia mode is not activated)
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
either by using clipboard or entering them interactively.`,
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
ValidArgs: []string{"get", "set"},
|
ValidArgs: []string{"get", "set"},
|
||||||
Args: cobra.ExactValidArgs(1),
|
Args: cobra.ExactValidArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "get":
|
case "get":
|
||||||
keys, err := qmkenc.QCmdGetKeys(qdev)
|
var keys []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
keys, err = qmkenc.QCmdGetKeys(qdev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
if subViper.GetBool("verify") {
|
||||||
|
var qenc []byte
|
||||||
|
var qdec []byte
|
||||||
|
verificationString := []byte("Im checking if the received key is correct")
|
||||||
|
enc, err := eqmkcommon.AesCBCEncrypt(verificationString, keys)
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.LOG_DEBUGF("Encrypted: len=%d %v\n", len(enc), enc)
|
||||||
|
if qenc, err = qmkenc.QCmdEncrypt(qdev, verificationString); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.LOG_DEBUGF("QEncrypted: len=%d %v\n", len(qenc), qenc)
|
||||||
|
dec, err := eqmkcommon.AesCBCDecrypt(qenc, keys)
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.LOG_DEBUGF("Decrypted: %s\n", dec)
|
||||||
|
if qdec, err = qmkenc.QCmdDecrypt(qdev, enc); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.LOG_DEBUGF("QDecrypted: %s\n", qdec)
|
||||||
|
|
||||||
|
if bytes.Compare(qdec, dec) != 0 {
|
||||||
|
slog.LOG_ERRORLN("Key verification failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.LOG_DEBUGF("keys: %v\n", keys)
|
||||||
if subViper.GetBool("human_readable") {
|
if subViper.GetBool("human_readable") {
|
||||||
for i := 0; i < len(keys); {
|
for i := 0; i < len(keys); {
|
||||||
if subViper.GetBool("hex_mode") {
|
if subViper.GetBool("hex_mode") {
|
||||||
|
@ -77,11 +146,26 @@ to quickly create a Cobra application.`,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "set":
|
case "set":
|
||||||
|
if subViper.GetBool("cin") {
|
||||||
|
if err := clipboard.Init(); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in, err := keysReadIn()
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = qmkenc.QCmdSetKeys(qdev, in); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Invalid command: %s", args[0])
|
slog.LOG_ERRORFLN("Invalid command: %s", args[0])
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +174,12 @@ func init() {
|
||||||
rootCmd.AddCommand(keysCmd)
|
rootCmd.AddCommand(keysCmd)
|
||||||
|
|
||||||
keysSetFlags = pflag.NewFlagSet("set", pflag.ExitOnError)
|
keysSetFlags = pflag.NewFlagSet("set", pflag.ExitOnError)
|
||||||
|
keysSetFlags.BoolP("cin", "c", true, "Read input from clipboard. If not set input will be read interactively")
|
||||||
|
|
||||||
keysGetFlags = pflag.NewFlagSet("get", pflag.ExitOnError)
|
keysGetFlags = pflag.NewFlagSet("get", pflag.ExitOnError)
|
||||||
keysGetFlags.BoolP("human_readable", "r", false, "Print the keys in a human readable format")
|
keysGetFlags.BoolP("human_readable", "r", false, "Print the keys in a human readable format")
|
||||||
keysGetFlags.BoolP("hex_mode", "x", true, "Print the keys in hex values")
|
keysGetFlags.BoolP("hex_mode", "x", true, "Print the keys in hex values")
|
||||||
|
keysGetFlags.BoolP("verify", "v", true, "Verify the received key")
|
||||||
|
|
||||||
var cmd string
|
var cmd string
|
||||||
reached := false
|
reached := false
|
||||||
|
@ -114,9 +200,11 @@ func init() {
|
||||||
keysCmd.Flags().AddFlagSet(keysGetFlags)
|
keysCmd.Flags().AddFlagSet(keysGetFlags)
|
||||||
subViper.BindPFlag("human_readable", keysCmd.Flags().Lookup("human_readable"))
|
subViper.BindPFlag("human_readable", keysCmd.Flags().Lookup("human_readable"))
|
||||||
subViper.BindPFlag("hex_mode", keysCmd.Flags().Lookup("hex_mode"))
|
subViper.BindPFlag("hex_mode", keysCmd.Flags().Lookup("hex_mode"))
|
||||||
|
subViper.BindPFlag("verify", keysCmd.Flags().Lookup("verify"))
|
||||||
break
|
break
|
||||||
case "set":
|
case "set":
|
||||||
keysCmd.Flags().AddFlagSet(keysSetFlags)
|
keysCmd.Flags().AddFlagSet(keysSetFlags)
|
||||||
|
subViper.BindPFlag("cin", keysCmd.Flags().Lookup("cin"))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
169
cmd/root.go
169
cmd/root.go
|
@ -19,9 +19,14 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitea.olznet.de/OlzNet/slog"
|
"gitea.olznet.de/OlzNet/slog"
|
||||||
|
"github.com/TheCreeper/go-notify"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
homedir "github.com/mitchellh/go-homedir"
|
homedir "github.com/mitchellh/go-homedir"
|
||||||
|
@ -31,7 +36,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var cfgFile string
|
var cfgFile string
|
||||||
var stdLogger *slog.StdoutLogger
|
var stdLogger slog.SLogLogI
|
||||||
|
var fileLogger slog.SLogLogI
|
||||||
var qdev *qmkenc.QEncDevice
|
var qdev *qmkenc.QEncDevice
|
||||||
var subViper *viper.Viper
|
var subViper *viper.Viper
|
||||||
|
|
||||||
|
@ -41,45 +47,118 @@ func initSubViper() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSLog() {
|
func initSLog() error {
|
||||||
slog.CLEAR_LOGGERS()
|
slog.CLEAR_LOGGERS()
|
||||||
slog.SET_LEVEL(slog.GetSLogLevel(viper.GetString("loglevel")))
|
slog.SET_LEVEL(slog.GetSLogLevel(viper.GetString("loglevel")))
|
||||||
|
|
||||||
if viper.GetBool("logenable") {
|
if viper.GetBool("logstdout") {
|
||||||
stdLogger = slog.NewStdoutLogger().(*slog.StdoutLogger)
|
stdLogger = slog.NewStdoutLogger()
|
||||||
slog.ADD_LOGGER(stdLogger)
|
slog.ADD_LOGGER(stdLogger)
|
||||||
}
|
}
|
||||||
|
if viper.GetString("logfile") != "" {
|
||||||
|
fileLogger, err := slog.NewFileLogger(viper.GetString("logfile"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slog.ADD_LOGGER(fileLogger)
|
||||||
|
}
|
||||||
|
|
||||||
if viper.GetBool("logverbose") {
|
if viper.GetBool("logverbose") {
|
||||||
slog.SET_VERBOSE(true)
|
slog.SET_VERBOSE(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if viper.GetString("loglevel") == slog.SLogLevelToString(slog.DEBUG) {
|
||||||
|
slog.LOG_WARNLN("!!! DEBUG ENABLED !!!")
|
||||||
|
slog.LOG_WARNLN("!!! There could be sensitive information logged !!!")
|
||||||
|
slog.LOG_WARNLN("!!! NEVER use this together with productive keys !!!")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initInfoType() {
|
||||||
|
switch viper.GetString("info_type") {
|
||||||
|
case "notify":
|
||||||
|
ntf := notify.NewNotification("eqmk request", "")
|
||||||
|
var err error
|
||||||
|
qmkenc.QEncInfoHandler = func(msg string) {
|
||||||
|
ntf.Body = msg
|
||||||
|
ntf.AppIcon = "dialog-question"
|
||||||
|
if ntf.ReplacesID, err = ntf.Show(); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qmkenc.QEncEndHandler = func() {
|
||||||
|
notify.CloseNotification(ntf.ReplacesID)
|
||||||
|
}
|
||||||
|
qmkenc.QEncErrHandler = func(err error) {
|
||||||
|
ntf.Body = err.Error()
|
||||||
|
ntf.AppIcon = "dialog-error"
|
||||||
|
if ntf.ReplacesID, err = ntf.Show(); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lock() error {
|
||||||
|
for {
|
||||||
|
if _, err := os.Stat(viper.GetString("lockfile")); err == nil {
|
||||||
|
slog.LOG_DEBUGLN("Waiting for lock")
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
continue
|
||||||
|
} else if os.IsNotExist(err) {
|
||||||
|
file, err := os.Create(viper.GetString("lockfile"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands
|
// rootCmd represents the base command when called without any subcommands
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "eqmk",
|
Use: "eqmk",
|
||||||
Short: "A brief description of your application",
|
Short: "command line client to interact with encryption enabled qmk device",
|
||||||
Long: `A longer description that spans multiple lines and likely contains
|
Long: `This tool is used to manage and interact with encryption enabled qmk devices.
|
||||||
examples and usage of using your application. For example:
|
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
It can retrieve information about the configuration and the running mode for the device.
|
||||||
This application is a tool to generate the needed files
|
Further it can decrypt, encrypt data and also change configuration of the device.
|
||||||
to quickly create a Cobra application.`,
|
There is also a keepassxc proxy included that can interact directly with keepassxc
|
||||||
|
to decrypt encrypted logins and passwords transparent for browsers.`,
|
||||||
// Uncomment the following line if your bare application
|
// Uncomment the following line if your bare application
|
||||||
// has an action associated with it:
|
// has an action associated with it:
|
||||||
// Run: func(cmd *cobra.Command, args []string) { },
|
// Run: func(cmd *cobra.Command, args []string) { },
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
if err := lock(); err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGKILL)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigChan
|
||||||
|
slog.LOG_DEBUGLN("Removing lockfile")
|
||||||
|
os.Remove(viper.GetString("lockfile"))
|
||||||
|
}()
|
||||||
|
|
||||||
slog.LOG_DEBUGLN("Root Pre run started")
|
slog.LOG_DEBUGLN("Root Pre run started")
|
||||||
|
initInfoType()
|
||||||
actDev := viper.GetString("device.path")
|
actDev := viper.GetString("device.path")
|
||||||
if actDev == "" {
|
if actDev == "" {
|
||||||
return fmt.Errorf("No device specified")
|
slog.LOG_ERRORLN("No device specified")
|
||||||
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if viper.GetInt("device.blocksize") == 0 {
|
if viper.GetInt("device.blocksize") == 0 {
|
||||||
qdevs, err := qmkenc.QEncScanDevices()
|
qdevs, err := qmkenc.QEncScanDevices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.LOG_ERRORLN(err)
|
slog.LOG_ERRORLN(err)
|
||||||
return err
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, qd := range qdevs {
|
for _, qd := range qdevs {
|
||||||
|
@ -91,27 +170,28 @@ to quickly create a Cobra application.`,
|
||||||
if qdev == nil {
|
if qdev == nil {
|
||||||
err = fmt.Errorf("Device: %s not found\n", actDev)
|
err = fmt.Errorf("Device: %s not found\n", actDev)
|
||||||
slog.LOG_ERRORF("%s\n", err)
|
slog.LOG_ERRORF("%s\n", err)
|
||||||
return err
|
os.Exit(-1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qdev = qmkenc.QEncNewDevice(actDev, uint8(viper.GetInt("device.blocksize")), viper.GetInt("device.timeout"), viper.GetInt("device.retry_timeout"), viper.GetInt("device.retry_wait"))
|
qdev = qmkenc.QEncNewDevice(actDev, uint8(viper.GetInt("device.blocksize")), viper.GetInt("device.timeout"), viper.GetInt("device.retry_timeout"), viper.GetInt("device.retry_wait"))
|
||||||
if err := qdev.Open(); err != nil {
|
if err := qdev.Open(); err != nil {
|
||||||
slog.LOG_ERRORLN(err)
|
slog.LOG_ERRORLN(err)
|
||||||
return err
|
os.Exit(-1)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
slog.LOG_DEBUGLN("Open")
|
slog.LOG_DEBUGLN("Open")
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) (err error) {
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
slog.LOG_DEBUGLN("Finished")
|
slog.LOG_DEBUGLN("Finished")
|
||||||
if qdev != nil {
|
if qdev != nil {
|
||||||
err = qdev.Close()
|
err := qdev.Close()
|
||||||
|
if err != nil {
|
||||||
|
slog.LOG_ERRORLN(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
slog.LOG_DEBUGLN("Removing lockfile")
|
||||||
|
os.Remove(viper.GetString("lockfile"))
|
||||||
slog.FINISH()
|
slog.FINISH()
|
||||||
|
|
||||||
return err
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,30 +207,50 @@ func Execute() {
|
||||||
func init() {
|
func init() {
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.eqmk/eqmk.conf)")
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.eqmk/eqmk.toml)")
|
||||||
rootCmd.PersistentFlags().StringP("level", "", "INFO", "Log level [ERROR, WARN, INFO, DEBUG]")
|
rootCmd.PersistentFlags().StringP("loglevel", "", "ERROR", "Log level [ERROR, WARN, INFO, DEBUG]")
|
||||||
rootCmd.PersistentFlags().BoolP("verbose", "", false, "Verbose logging")
|
rootCmd.PersistentFlags().BoolP("verbose", "", false, "Verbose logging")
|
||||||
rootCmd.PersistentFlags().BoolP("log", "", false, "Enable logging")
|
rootCmd.PersistentFlags().BoolP("logstdout", "", false, "Enable logging to stdout")
|
||||||
|
rootCmd.PersistentFlags().StringP("logfile", "", "", "Enable logging to file")
|
||||||
|
rootCmd.PersistentFlags().StringP("lockfile", "", "/tmp/eqmk.lock", "Lock file to prevent multiple accessing device")
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringP("device", "d", "", "qmk device path")
|
rootCmd.PersistentFlags().StringP("device", "d", "", "qmk device path")
|
||||||
rootCmd.PersistentFlags().IntP("blocksize", "", 0, "qmk device blocksize")
|
rootCmd.PersistentFlags().IntP("blocksize", "", 0, "qmk device blocksize")
|
||||||
rootCmd.PersistentFlags().IntP("timeout", "", 5, "qmk device retry timeout")
|
rootCmd.PersistentFlags().IntP("device_timeout", "", 5, "qmk device retry timeout")
|
||||||
rootCmd.PersistentFlags().IntP("retry_timeout", "", 10, "qmk device retry timeout")
|
rootCmd.PersistentFlags().IntP("retry_timeout", "", 10, "qmk device retry timeout")
|
||||||
rootCmd.PersistentFlags().IntP("retry_wait", "", 1, "qmk device retry wait time")
|
rootCmd.PersistentFlags().IntP("retry_wait", "", 1, "qmk device retry wait time")
|
||||||
|
|
||||||
viper.BindPFlag("loglevel", rootCmd.PersistentFlags().Lookup("level"))
|
rootCmd.PersistentFlags().StringP("info_type", "", "notify", "type for request messages ([notfiy|none])")
|
||||||
|
|
||||||
|
viper.BindPFlag("loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))
|
||||||
viper.BindPFlag("logverbose", rootCmd.PersistentFlags().Lookup("verbose"))
|
viper.BindPFlag("logverbose", rootCmd.PersistentFlags().Lookup("verbose"))
|
||||||
|
|
||||||
viper.BindPFlag("device.path", rootCmd.PersistentFlags().Lookup("device"))
|
viper.BindPFlag("device.path", rootCmd.PersistentFlags().Lookup("device"))
|
||||||
viper.BindPFlag("device.blocksize", rootCmd.PersistentFlags().Lookup("blocksize"))
|
viper.BindPFlag("device.blocksize", rootCmd.PersistentFlags().Lookup("blocksize"))
|
||||||
viper.BindPFlag("device.timeout", rootCmd.PersistentFlags().Lookup("timeout"))
|
viper.BindPFlag("device.timeout", rootCmd.PersistentFlags().Lookup("device_timeout"))
|
||||||
viper.BindPFlag("device.retry_timeout", rootCmd.PersistentFlags().Lookup("retry_timeout"))
|
viper.BindPFlag("device.retry_timeout", rootCmd.PersistentFlags().Lookup("retry_timeout"))
|
||||||
viper.BindPFlag("device.retry_wait", rootCmd.PersistentFlags().Lookup("retry_wait"))
|
viper.BindPFlag("device.retry_wait", rootCmd.PersistentFlags().Lookup("retry_wait"))
|
||||||
viper.BindPFlag("logenable", rootCmd.PersistentFlags().Lookup("log"))
|
viper.BindPFlag("logstdout", rootCmd.PersistentFlags().Lookup("logstdout"))
|
||||||
|
viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile"))
|
||||||
|
viper.BindPFlag("lockfile", rootCmd.PersistentFlags().Lookup("lockfile"))
|
||||||
|
|
||||||
viper.SetDefault("loglevel", "INFO")
|
viper.BindPFlag("info_type", rootCmd.PersistentFlags().Lookup("info_type"))
|
||||||
|
|
||||||
|
viper.SetDefault("loglevel", "ERROR")
|
||||||
viper.SetDefault("logverbose", false)
|
viper.SetDefault("logverbose", false)
|
||||||
viper.SetDefault("logenable", false)
|
viper.SetDefault("logstdout", false)
|
||||||
|
|
||||||
|
viper.SetDefault("info_type", "notify")
|
||||||
|
|
||||||
|
// crappy native-messaging-hosts ...
|
||||||
|
if (len(os.Args) == 2 && strings.HasPrefix(os.Args[1], "chrome-extension")) || // chrome
|
||||||
|
(len(os.Args) == 3 && strings.HasPrefix(os.Args[2], "keepassxc-browser@keepassxc.org")) { // firefox
|
||||||
|
os.Args = []string{os.Args[0], "keepassxcproxy"}
|
||||||
|
}
|
||||||
|
if len(os.Args) >= 2 && os.Args[1] == "keepassxcproxy" {
|
||||||
|
// we have to override logging as we cannot write to stdout
|
||||||
|
viper.Set("logstdout", false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
@ -185,6 +285,9 @@ func initConfig() {
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
initSLog()
|
if err := initSLog(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
initSubViper()
|
initSubViper()
|
||||||
}
|
}
|
||||||
|
|
10
cmd/scan.go
10
cmd/scan.go
|
@ -27,13 +27,9 @@ import (
|
||||||
// scanCmd represents the scan command
|
// scanCmd represents the scan command
|
||||||
var scanCmd = &cobra.Command{
|
var scanCmd = &cobra.Command{
|
||||||
Use: "scan",
|
Use: "scan",
|
||||||
Short: "A brief description of your command",
|
Short: "Scan for encryption enabled qmk devices",
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
Long: `It will scan all usb hid devices and check
|
||||||
and usage of using your command. For example:
|
if they are enabled for encryption.`,
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package eqmkcommon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AesCBCEncrypt(data, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blocksize := block.BlockSize()
|
||||||
|
pdata, err := pkcs7Pad(data, blocksize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encdata := make([]byte, blocksize+len(pdata))
|
||||||
|
iv := encdata[:blocksize]
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
mode.CryptBlocks(encdata[blocksize:], pdata)
|
||||||
|
|
||||||
|
return encdata, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AesCBCDecrypt(data, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blocksize := block.BlockSize()
|
||||||
|
|
||||||
|
if len(data) < blocksize {
|
||||||
|
return nil, fmt.Errorf("Cipher text too short")
|
||||||
|
}
|
||||||
|
iv := data[:blocksize]
|
||||||
|
data = data[blocksize:]
|
||||||
|
|
||||||
|
if len(data)%blocksize != 0 {
|
||||||
|
return nil, fmt.Errorf("Cipher text is not a multiple of block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
mode.CryptBlocks(data, data)
|
||||||
|
data, err = pkcs7Unpad(data, blocksize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package eqmkcommon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PKCS7 padding.
|
||||||
|
|
||||||
|
// PKCS7 errors.
|
||||||
|
var (
|
||||||
|
// ErrInvalidBlockSize indicates hash blocksize <= 0.
|
||||||
|
ErrInvalidBlockSize = errors.New("invalid blocksize")
|
||||||
|
|
||||||
|
// ErrInvalidPKCS7Data indicates bad input to PKCS7 pad or unpad.
|
||||||
|
ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data (empty or not padded)")
|
||||||
|
|
||||||
|
// ErrInvalidPKCS7Padding indicates PKCS7 unpad fails to bad input.
|
||||||
|
ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
|
||||||
|
)
|
||||||
|
|
||||||
|
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
|
||||||
|
// n is the block size. The size of the result is x times n, where x
|
||||||
|
// is at least 1.
|
||||||
|
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
|
||||||
|
if blocksize <= 0 {
|
||||||
|
return nil, ErrInvalidBlockSize
|
||||||
|
}
|
||||||
|
if b == nil || len(b) == 0 {
|
||||||
|
return nil, ErrInvalidPKCS7Data
|
||||||
|
}
|
||||||
|
n := blocksize - (len(b) % blocksize)
|
||||||
|
pb := make([]byte, len(b)+n)
|
||||||
|
copy(pb, b)
|
||||||
|
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
|
||||||
|
return pb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs7Unpad validates and unpads data from the given bytes slice.
|
||||||
|
// The returned value will be 1 to n bytes smaller depending on the
|
||||||
|
// amount of padding, where n is the block size.
|
||||||
|
func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
|
||||||
|
if blocksize <= 0 {
|
||||||
|
return nil, ErrInvalidBlockSize
|
||||||
|
}
|
||||||
|
if b == nil || len(b) == 0 {
|
||||||
|
return nil, ErrInvalidPKCS7Data
|
||||||
|
}
|
||||||
|
if len(b)%blocksize != 0 {
|
||||||
|
return nil, ErrInvalidPKCS7Padding
|
||||||
|
}
|
||||||
|
c := b[len(b)-1]
|
||||||
|
n := int(c)
|
||||||
|
if n == 0 || n > len(b) {
|
||||||
|
return nil, ErrInvalidPKCS7Padding
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if b[len(b)-n+i] != c {
|
||||||
|
return nil, ErrInvalidPKCS7Padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b[:len(b)-n], nil
|
||||||
|
}
|
12
go.mod
12
go.mod
|
@ -1,22 +1,33 @@
|
||||||
module gitea.olznet.de/OlzNet/eqmk
|
module gitea.olznet.de/OlzNet/eqmk
|
||||||
|
|
||||||
|
replace gitea.olznet.de/OlzNet/slog => ../slog
|
||||||
|
|
||||||
replace gitea.olznet.de/OlzNet/qmkenc => ../qmkenc
|
replace gitea.olznet.de/OlzNet/qmkenc => ../qmkenc
|
||||||
|
|
||||||
|
replace gitea.olznet.de/OlzNet/golang-keepassxc-browser => ../golang-keepassxc-browser
|
||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
gitea.olznet.de/OlzNet/golang-keepassxc-browser v0.0.0-00010101000000-000000000000
|
||||||
gitea.olznet.de/OlzNet/qmkenc v0.0.0-00010101000000-000000000000
|
gitea.olznet.de/OlzNet/qmkenc v0.0.0-00010101000000-000000000000
|
||||||
gitea.olznet.de/OlzNet/slog v1.2.4
|
gitea.olznet.de/OlzNet/slog v1.2.4
|
||||||
|
github.com/TheCreeper/go-notify v0.2.0
|
||||||
|
github.com/manifoldco/promptui v0.9.0
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/spf13/cobra v1.4.0
|
github.com/spf13/cobra v1.4.0
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.11.0
|
github.com/spf13/viper v1.11.0
|
||||||
golang.design/x/clipboard v0.6.2
|
golang.design/x/clipboard v0.6.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.0.3 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/jamesruan/sodium v1.0.14 // indirect
|
||||||
github.com/magiconair/properties v1.8.6 // indirect
|
github.com/magiconair/properties v1.8.6 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
|
@ -24,7 +35,6 @@ require (
|
||||||
github.com/spf13/afero v1.8.2 // indirect
|
github.com/spf13/afero v1.8.2 // indirect
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/sstallion/go-hid v0.0.0-20211019232252-c64377bfa49e // indirect
|
github.com/sstallion/go-hid v0.0.0-20211019232252-c64377bfa49e // indirect
|
||||||
github.com/subosito/gotenv v1.2.0 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 // indirect
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 // indirect
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -36,13 +36,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
gitea.olznet.de/OlzNet/slog v1.2.4 h1:22/+57/2J7EnFQAwT6xRENgvPeI2RwSmOWuXwkujpf4=
|
|
||||||
gitea.olznet.de/OlzNet/slog v1.2.4/go.mod h1:xHB0ZnXIXFdnISKpWvDnrBSl4dGB3rzQji36n2sbJXg=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/TheCreeper/go-notify v0.2.0 h1:akzlSD8IWx+uOZqGNwS0FYsThYuw11JkXsYiQXY5kgo=
|
||||||
|
github.com/TheCreeper/go-notify v0.2.0/go.mod h1:paZnY8fMbaOyZLQWJitGWAMrO5ot3Ow7id47cyEL1KA=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
@ -63,6 +66,8 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
@ -125,6 +130,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jamesruan/sodium v1.0.14 h1:JfOHobip/lUWouxHV3PwYwu3gsLewPrDrZXO3HuBzUU=
|
||||||
|
github.com/jamesruan/sodium v1.0.14/go.mod h1:GK2+LACf7kuVQ9k7Irk0MB2B65j5rVqkz+9ylGIggZk=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
@ -136,6 +143,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
|
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||||
|
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||||
|
@ -287,6 +296,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
Loading…
Reference in New Issue