Base features done

This commit is contained in:
Matthias Fulz 2022-06-26 23:34:41 +02:00
parent 12f205fae4
commit 08d20361bf
11 changed files with 605 additions and 119 deletions

View File

@ -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
}, },
} }

View File

@ -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
} }
}, },
} }

View File

@ -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)
} }
}, },
} }

126
cmd/keepassxcproxy.go Normal file
View File

@ -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")
}

View File

@ -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
} }
} }

View File

@ -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()
} }

View File

@ -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
}, },

60
eqmkcommon/crypt.go Normal file
View File

@ -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
}

63
eqmkcommon/pkcs7.go Normal file
View File

@ -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
View File

@ -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
View File

@ -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=