Base features done
This commit is contained in:
parent
12f205fae4
commit
08d20361bf
56
cmd/cnf.go
56
cmd/cnf.go
|
@ -21,8 +21,11 @@ import (
|
|||
"strings"
|
||||
|
||||
"gitea.olznet.de/OlzNet/qmkenc"
|
||||
"gitea.olznet.de/OlzNet/slog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
var cnfSetFlags *pflag.FlagSet
|
||||
|
@ -45,25 +48,27 @@ func cfgFormat(cfg *qmkenc.QCfg) string {
|
|||
// cnfCmd represents the cnf command
|
||||
var cnfCmd = &cobra.Command{
|
||||
Use: "cnf [get|set] [flags]",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
Short: "Manage configuration for encryption enabled qmk devices",
|
||||
Long: `Via get the actual configuration can be retrieved. This
|
||||
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.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Set can be used to change the configuration of the device.
|
||||
If enabling paranoia mode of a device be aware that this
|
||||
cannot be undone without loosing your keys.`,
|
||||
ValidArgs: []string{"get", "set"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
switch args[0] {
|
||||
case "get":
|
||||
cfg, err := qmkenc.QCmdGetCfg(qdev)
|
||||
if err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
if subViper.GetBool("format") {
|
||||
fmt.Println(cfgFormat(cfg))
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if subViper.GetBool("max_error") {
|
||||
fmt.Printf("%d\n", cfg.MaxError())
|
||||
|
@ -88,35 +93,52 @@ to quickly create a Cobra application.`,
|
|||
f := cnfSetFlags.Lookup("max_error")
|
||||
if f != nil && f.Changed {
|
||||
if err := qmkenc.QCmdSetCfgMaxError(qdev, uint8(subViper.GetInt("max_error"))); err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
f = cnfSetFlags.Lookup("paranoia_mode")
|
||||
if f != nil && f.Changed {
|
||||
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
|
||||
}
|
||||
if err := qmkenc.QCmdSetCfgParanoia(qdev); err != nil {
|
||||
return err
|
||||
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 {
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
f = cnfSetFlags.Lookup("secure_mode")
|
||||
if f != nil && f.Changed {
|
||||
if err := qmkenc.QCmdSetCfgSecure(qdev, subViper.GetBool("secure_mode")); err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
f = cnfSetFlags.Lookup("timeout_min")
|
||||
if f != nil && f.Changed {
|
||||
if err := qmkenc.QCmdSetCfgTimeout(qdev, uint8(subViper.GetInt("timeout_min"))); err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"golang.design/x/clipboard"
|
||||
|
||||
"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") {
|
||||
in = clipboard.Read(clipboard.FmtText)
|
||||
} else {
|
||||
|
@ -68,61 +68,58 @@ func writeOut(out []byte) (c <-chan struct{}, err error) {
|
|||
// cryptCmd represents the crypt command
|
||||
var cryptCmd = &cobra.Command{
|
||||
Use: "crypt [encrypt|decrypt] [flags]",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
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.`,
|
||||
Short: "Encrypt or decrypt data by using the qmk device",
|
||||
Long: `The data can be provided via clipboard or stdin and
|
||||
by using base64 encoding.
|
||||
The default will use clipboard without base64 encoding.`,
|
||||
ValidArgs: []string{"encrypt", "decrypt"},
|
||||
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 err := clipboard.Init(); err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
in, err := readIn()
|
||||
in, err := cryptReadIn()
|
||||
if err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
var out []byte
|
||||
|
||||
switch args[0] {
|
||||
case "encrypt":
|
||||
if err = qmkenc.QCmdEncrypt(qdev, in); err != nil {
|
||||
return err
|
||||
if out, err = qmkenc.QCmdEncrypt(qdev, in); err != nil {
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
break
|
||||
case "decrypt":
|
||||
if err = qmkenc.QCmdDecrypt(qdev, in); err != nil {
|
||||
return err
|
||||
if out, err = qmkenc.QCmdDecrypt(qdev, in); err != nil {
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("Invalid command: %s", args[0])
|
||||
}
|
||||
out, err := qmkenc.QCmdGetBuffer(qdev)
|
||||
if err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(fmt.Errorf("Invalid command: %s", args[0]))
|
||||
return
|
||||
}
|
||||
|
||||
c, err := writeOut(out)
|
||||
if err != nil {
|
||||
return err
|
||||
slog.LOG_ERRORLN(err)
|
||||
return
|
||||
}
|
||||
if c == nil {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
return nil
|
||||
case <-time.After(time.Second * time.Duration(viper.GetInt("timeout"))):
|
||||
return nil
|
||||
return
|
||||
case <-time.After(time.Second * time.Duration(subViper.GetInt("timeout"))):
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
39
cmd/dev.go
39
cmd/dev.go
|
@ -19,31 +19,42 @@ import (
|
|||
"fmt"
|
||||
|
||||
"gitea.olznet.de/OlzNet/qmkenc"
|
||||
"gitea.olznet.de/OlzNet/slog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// devCmd represents the dev command
|
||||
var devCmd = &cobra.Command{
|
||||
Use: "dev [unlock|lock|initialize|reset] [flags]",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
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.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Use: "dev [unlock|lock|initialize|reset|mode] [flags]",
|
||||
Short: "Device commands used to manage basic aspects",
|
||||
Long: `This command is used to lock, unlock, initialize, reset
|
||||
or retrieving the run mode of the device.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
slog.LOG_ERRORLN("No command")
|
||||
return
|
||||
}
|
||||
var err error
|
||||
switch args[0] {
|
||||
case "unlock":
|
||||
return qmkenc.QCmdUnlock(qdev)
|
||||
err = qmkenc.QCmdUnlock(qdev)
|
||||
case "lock":
|
||||
return qmkenc.QCmdLock(qdev)
|
||||
err = qmkenc.QCmdLock(qdev)
|
||||
case "initialize":
|
||||
return qmkenc.QCmdInitialize(qdev)
|
||||
err = qmkenc.QCmdInitialize(qdev)
|
||||
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:
|
||||
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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitea.olznet.de/OlzNet/eqmk/eqmkcommon"
|
||||
"gitea.olznet.de/OlzNet/qmkenc"
|
||||
"gitea.olznet.de/OlzNet/slog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.design/x/clipboard"
|
||||
)
|
||||
|
||||
var keysSetFlags *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
|
||||
var keysCmd = &cobra.Command{
|
||||
Use: "keys [get|set] [flags]",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
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.`,
|
||||
Short: "Managing cryptographic keys of the qmk device",
|
||||
Long: `Retrieving the keys via get to store them in a safe place.
|
||||
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)
|
||||
either by using clipboard or entering them interactively.`,
|
||||
ValidArgs: []string{"get", "set"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
switch args[0] {
|
||||
case "get":
|
||||
keys, err := qmkenc.QCmdGetKeys(qdev)
|
||||
var keys []byte
|
||||
var err error
|
||||
|
||||
keys, err = qmkenc.QCmdGetKeys(qdev)
|
||||
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") {
|
||||
for i := 0; i < len(keys); {
|
||||
if subViper.GetBool("hex_mode") {
|
||||
|
@ -77,11 +146,26 @@ to quickly create a Cobra application.`,
|
|||
}
|
||||
break
|
||||
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
|
||||
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)
|
||||
|
||||
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.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("verify", "v", true, "Verify the received key")
|
||||
|
||||
var cmd string
|
||||
reached := false
|
||||
|
@ -114,9 +200,11 @@ func init() {
|
|||
keysCmd.Flags().AddFlagSet(keysGetFlags)
|
||||
subViper.BindPFlag("human_readable", keysCmd.Flags().Lookup("human_readable"))
|
||||
subViper.BindPFlag("hex_mode", keysCmd.Flags().Lookup("hex_mode"))
|
||||
subViper.BindPFlag("verify", keysCmd.Flags().Lookup("verify"))
|
||||
break
|
||||
case "set":
|
||||
keysCmd.Flags().AddFlagSet(keysSetFlags)
|
||||
subViper.BindPFlag("cin", keysCmd.Flags().Lookup("cin"))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
175
cmd/root.go
175
cmd/root.go
|
@ -19,9 +19,14 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"gitea.olznet.de/OlzNet/slog"
|
||||
"github.com/TheCreeper/go-notify"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
|
@ -31,7 +36,8 @@ import (
|
|||
)
|
||||
|
||||
var cfgFile string
|
||||
var stdLogger *slog.StdoutLogger
|
||||
var stdLogger slog.SLogLogI
|
||||
var fileLogger slog.SLogLogI
|
||||
var qdev *qmkenc.QEncDevice
|
||||
var subViper *viper.Viper
|
||||
|
||||
|
@ -41,45 +47,118 @@ func initSubViper() {
|
|||
}
|
||||
}
|
||||
|
||||
func initSLog() {
|
||||
func initSLog() error {
|
||||
slog.CLEAR_LOGGERS()
|
||||
slog.SET_LEVEL(slog.GetSLogLevel(viper.GetString("loglevel")))
|
||||
|
||||
if viper.GetBool("logenable") {
|
||||
stdLogger = slog.NewStdoutLogger().(*slog.StdoutLogger)
|
||||
if viper.GetBool("logstdout") {
|
||||
stdLogger = slog.NewStdoutLogger()
|
||||
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") {
|
||||
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
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "eqmk",
|
||||
Short: "A brief description of your application",
|
||||
Long: `A longer description that spans multiple lines and likely contains
|
||||
examples and usage of using your application. For example:
|
||||
Short: "command line client to interact with encryption enabled qmk device",
|
||||
Long: `This tool is used to manage and interact with encryption enabled qmk devices.
|
||||
|
||||
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.`,
|
||||
It can retrieve information about the configuration and the running mode for the device.
|
||||
Further it can decrypt, encrypt data and also change configuration of the device.
|
||||
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
|
||||
// has an action associated with it:
|
||||
// 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")
|
||||
initInfoType()
|
||||
actDev := viper.GetString("device.path")
|
||||
if actDev == "" {
|
||||
return fmt.Errorf("No device specified")
|
||||
slog.LOG_ERRORLN("No device specified")
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
if viper.GetInt("device.blocksize") == 0 {
|
||||
qdevs, err := qmkenc.QEncScanDevices()
|
||||
if err != nil {
|
||||
slog.LOG_ERRORLN(err)
|
||||
return err
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
for _, qd := range qdevs {
|
||||
|
@ -91,27 +170,28 @@ to quickly create a Cobra application.`,
|
|||
if qdev == nil {
|
||||
err = fmt.Errorf("Device: %s not found\n", actDev)
|
||||
slog.LOG_ERRORF("%s\n", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
slog.LOG_ERRORLN(err)
|
||||
return err
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
slog.LOG_ERRORLN(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
slog.LOG_DEBUGLN("Open")
|
||||
|
||||
return nil
|
||||
time.Sleep(2 * time.Second)
|
||||
},
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
slog.LOG_DEBUGLN("Finished")
|
||||
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()
|
||||
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -127,30 +207,50 @@ func Execute() {
|
|||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.eqmk/eqmk.conf)")
|
||||
rootCmd.PersistentFlags().StringP("level", "", "INFO", "Log level [ERROR, WARN, INFO, DEBUG]")
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.eqmk/eqmk.toml)")
|
||||
rootCmd.PersistentFlags().StringP("loglevel", "", "ERROR", "Log level [ERROR, WARN, INFO, DEBUG]")
|
||||
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().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_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("device.path", rootCmd.PersistentFlags().Lookup("device"))
|
||||
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_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("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.
|
||||
|
@ -185,6 +285,9 @@ func initConfig() {
|
|||
if err := viper.ReadInConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
initSLog()
|
||||
if err := initSLog(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
initSubViper()
|
||||
}
|
||||
|
|
10
cmd/scan.go
10
cmd/scan.go
|
@ -27,13 +27,9 @@ import (
|
|||
// scanCmd represents the scan command
|
||||
var scanCmd = &cobra.Command{
|
||||
Use: "scan",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
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.`,
|
||||
Short: "Scan for encryption enabled qmk devices",
|
||||
Long: `It will scan all usb hid devices and check
|
||||
if they are enabled for encryption.`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
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
|
||||
|
||||
replace gitea.olznet.de/OlzNet/slog => ../slog
|
||||
|
||||
replace gitea.olznet.de/OlzNet/qmkenc => ../qmkenc
|
||||
|
||||
replace gitea.olznet.de/OlzNet/golang-keepassxc-browser => ../golang-keepassxc-browser
|
||||
|
||||
go 1.18
|
||||
|
||||
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/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/spf13/cobra v1.4.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.11.0
|
||||
golang.design/x/clipboard v0.6.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // 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/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jamesruan/sodium v1.0.14 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // 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/cast v1.4.1 // 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/subosito/gotenv v1.2.0 // 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.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
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/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/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
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/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/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=
|
||||
|
@ -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/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/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/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=
|
||||
|
@ -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/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
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.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
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/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
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/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
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-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-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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
Loading…
Reference in New Issue