diff --git a/cli/commands/decrypt.go b/cli/commands/decrypt.go index f5ef1bd..9e5d30d 100644 --- a/cli/commands/decrypt.go +++ b/cli/commands/decrypt.go @@ -9,7 +9,6 @@ import ( "github.com/spf13/cobra" "github.com/mitchell/selfpass/cli/types" - "github.com/mitchell/selfpass/credentials/commands" "github.com/mitchell/selfpass/crypto" ) @@ -35,7 +34,7 @@ the new file.`, contents, err := ioutil.ReadFile(file) check(err) - key, err := hex.DecodeString(cfg.GetString(commands.KeyPrivateKey)) + key, err := hex.DecodeString(cfg.GetString(types.KeyPrivateKey)) check(err) passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key)) diff --git a/cli/commands/decrypt_cfg.go b/cli/commands/decrypt_cfg.go index 860c6b8..e0663a7 100644 --- a/cli/commands/decrypt_cfg.go +++ b/cli/commands/decrypt_cfg.go @@ -1,6 +1,8 @@ package commands import ( + "fmt" + "github.com/spf13/cobra" "github.com/mitchell/selfpass/cli/types" @@ -16,7 +18,9 @@ func makeDecryptCfg(repo types.ConfigRepo) *cobra.Command { _, _, err := repo.OpenConfig() check(err) - repo.DecryptConfig() + check(repo.DecryptConfig()) + + fmt.Println("Config decrypted. It will automatically encrypt next run of spc.") }, } diff --git a/cli/commands/encrypt.go b/cli/commands/encrypt.go index d9be9e5..440a187 100644 --- a/cli/commands/encrypt.go +++ b/cli/commands/encrypt.go @@ -9,7 +9,6 @@ import ( "github.com/spf13/cobra" "github.com/mitchell/selfpass/cli/types" - "github.com/mitchell/selfpass/credentials/commands" "github.com/mitchell/selfpass/crypto" ) @@ -31,7 +30,7 @@ new file.`, contents, err := ioutil.ReadFile(file) check(err) - key, err := hex.DecodeString(cfg.GetString(commands.KeyPrivateKey)) + key, err := hex.DecodeString(cfg.GetString(types.KeyPrivateKey)) check(err) passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key)) diff --git a/cli/commands/init.go b/cli/commands/init.go index d6d7a6c..774bae1 100644 --- a/cli/commands/init.go +++ b/cli/commands/init.go @@ -6,12 +6,10 @@ import ( "strings" "github.com/google/uuid" - "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "gopkg.in/AlecAivazis/survey.v1" "github.com/mitchell/selfpass/cli/types" - "github.com/mitchell/selfpass/credentials/commands" ) func makeInit(repo types.ConfigRepo) *cobra.Command { @@ -72,23 +70,16 @@ the users private key, and server certificates. (All of which will be encrypted) key, err := ioutil.ReadFile(keyFile) check(err) - cfg.Set(keyConnConfig, map[string]string{ + cfg.Set(types.KeyConnConfig, map[string]string{ "target": target, "ca": string(ca), "cert": string(cert), "key": string(key), }) - cfg.Set(commands.KeyPrivateKey, privateKey) + cfg.Set(types.KeyPrivateKey, privateKey) - if err := cfg.WriteConfig(); err != nil { - home, err := homedir.Dir() - check(err) - - check(cfg.WriteConfigAs(home + "/.spc.toml")) - cfg.SetConfigFile(home + "/.spc.toml") - fmt.Println("Wrote new config to: " + home + "/.spc.toml") - } + check(repo.WriteConfig()) }, } diff --git a/cli/commands/root.go b/cli/commands/root.go index 182bb3b..b895108 100644 --- a/cli/commands/root.go +++ b/cli/commands/root.go @@ -26,8 +26,6 @@ can interact with the entire Selfpass API.`, cfgFile := rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.spc.toml)") mgr := repositories.NewConfigManager(cfgFile) - defer mgr.CloseConfig() - clientInit := credrepos.NewCredentialServiceClient rootCmd.AddCommand(makeInit(mgr)) @@ -47,7 +45,7 @@ func makeInitClient(repo types.ConfigRepo, initClient credtypes.CredentialClient _, cfg, err := repo.OpenConfig() check(err) - connConfig := cfg.GetStringMapString(keyConnConfig) + connConfig := cfg.GetStringMapString(types.KeyConnConfig) client, err := initClient( ctx, @@ -68,5 +66,3 @@ func check(err error) { os.Exit(1) } } - -const keyConnConfig = "connection" diff --git a/cli/repositories/config.go b/cli/repositories/config.go index 6d68375..ef16dc4 100644 --- a/cli/repositories/config.go +++ b/cli/repositories/config.go @@ -1,6 +1,7 @@ package repositories import ( + "bytes" "fmt" "io/ioutil" "os" @@ -20,8 +21,6 @@ func NewConfigManager(cfgFile *string) *ConfigManager { type ConfigManager struct { masterpass string - decrypted bool - decrypt bool cfgFile *string v *viper.Viper } @@ -36,110 +35,110 @@ func (mgr *ConfigManager) OpenConfig() (output string, v *viper.Viper, err error } cfg := *mgr.cfgFile - v = viper.New() - mgr.v = v + mgr.v = viper.New() + mgr.v.SetConfigType("toml") - v.SetConfigType("toml") - - if cfg != "" { - // Use config file from the flag. - v.SetConfigFile(cfg) - } else { - // Find home directory. + if cfg == "" { home, err := homedir.Dir() if err != nil { return output, nil, err } - // Search config in home directory with name ".spc" (without extension). - v.AddConfigPath(home) - v.SetConfigName(".spc") - cfg = home + "/.spc.toml" } - if _, err := os.Open(cfg); !os.IsNotExist(err) { - prompt := &survey.Password{Message: "Master password:"} - if err = survey.AskOne(prompt, &mgr.masterpass, nil); err != nil { - return output, nil, err - } + mgr.v.SetConfigFile(cfg) + mgr.cfgFile = &cfg - mgr.decrypted, err = decryptConfig(mgr.masterpass, cfg) - if err != nil { - return output, nil, err - } + var contents []byte + var wasNotEncrypted bool + + if _, err := os.Open(cfg); os.IsNotExist(err) { + return output, mgr.v, fmt.Errorf("no config found, run 'init' command") + } + + prompt := &survey.Password{Message: "Master password:"} + if err = survey.AskOne(prompt, &mgr.masterpass, nil); err != nil { + return output, nil, err + } + + contents, err = mgr.decryptConfig(mgr.masterpass, cfg) + if err != nil && err.Error() == "ciphertext is not a multiple of the block size" { + fmt.Println("Config wasn't encrypted.") + wasNotEncrypted = true + } else if err != nil { + return output, nil, err } // v.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in. - if err = v.ReadInConfig(); err != nil { - mgr.decrypted = true - return output, v, fmt.Errorf("no config found, run 'init' command") + if err = mgr.v.ReadConfig(bytes.NewBuffer(contents)); err != nil { + return output, mgr.v, err + } + + if wasNotEncrypted { + if err = mgr.WriteConfig(); err != nil { + return output, nil, err + } } return mgr.masterpass, mgr.v, nil } -func decryptConfig(masterpass string, cfgFile string) (decrypted bool, err error) { - contents, err := ioutil.ReadFile(cfgFile) +func (mgr ConfigManager) decryptConfig(masterpass string, cfgFile string) (contents []byte, err error) { + contents, err = ioutil.ReadFile(cfgFile) if err != nil { - return decrypted, err + return contents, err } passkey, err := crypto.GenerateKeyFromPassword([]byte(masterpass)) if err != nil { - return decrypted, err + return contents, err } - contents, err = crypto.CBCDecrypt(passkey, contents) + plaintext, err := crypto.CBCDecrypt(passkey, contents) if err != nil && err.Error() == "Padding incorrect" { - return decrypted, fmt.Errorf("incorrect master password") - } else if err != nil && err.Error() == "ciphertext is not a multiple of the block size" { - fmt.Println("Config wasn't encrypted.") - return true, nil + return contents, fmt.Errorf("incorrect master password") + } else if err != nil { + return contents, err } + + return plaintext, nil +} + +func (mgr ConfigManager) DecryptConfig() error { + if err := mgr.v.WriteConfig(); err != nil { + return err + } + + return nil +} + +func (mgr ConfigManager) WriteConfig() (err error) { + if err := mgr.v.WriteConfigAs(*mgr.cfgFile); err != nil { + return err + } + + contents, err := ioutil.ReadFile(mgr.v.ConfigFileUsed()) if err != nil { - return decrypted, err + return err } - if err = ioutil.WriteFile(cfgFile, contents, 0600); err != nil { - return decrypted, err + keypass, err := crypto.GenerateKeyFromPassword([]byte(mgr.masterpass)) + if err != nil { + return err } - return true, nil -} - -func (mgr *ConfigManager) DecryptConfig() { - mgr.decrypt = true -} - -func (mgr *ConfigManager) CloseConfig() { - if !mgr.decrypt && mgr.decrypted { - contents, err := ioutil.ReadFile(mgr.v.ConfigFileUsed()) - if os.IsNotExist(err) { - return - } - - keypass, err := crypto.GenerateKeyFromPassword([]byte(mgr.masterpass)) - if err != nil { - panic(err) - } - - contents, err = crypto.CBCEncrypt(keypass, contents) - if err != nil { - panic(err) - } - - err = ioutil.WriteFile(mgr.v.ConfigFileUsed(), contents, 0600) - if err != nil { - panic(err) - } - - return - } - - if mgr.decrypt { - fmt.Println("Decrypting config file. It will auto-encrypt when you next run spc.") - } + contents, err = crypto.CBCEncrypt(keypass, contents) + if err != nil { + return err + } + + err = ioutil.WriteFile(mgr.v.ConfigFileUsed(), contents, 0600) + if err != nil { + return err + } + + return nil } diff --git a/cli/types/types.go b/cli/types/types.go index e0b5546..14fbe5c 100644 --- a/cli/types/types.go +++ b/cli/types/types.go @@ -4,6 +4,10 @@ import "github.com/spf13/viper" type ConfigRepo interface { OpenConfig() (masterpass string, cfg *viper.Viper, err error) - DecryptConfig() + DecryptConfig() (err error) SetMasterpass(masterpass string) + WriteConfig() (err error) } + +const KeyPrivateKey = "private_key" +const KeyConnConfig = "connection" diff --git a/credentials/commands/commands.go b/credentials/commands/commands.go index 8c8fadc..844f960 100644 --- a/credentials/commands/commands.go +++ b/credentials/commands/commands.go @@ -16,5 +16,3 @@ func check(err error) { os.Exit(1) } } - -const KeyPrivateKey = "private_key" diff --git a/credentials/commands/create.go b/credentials/commands/create.go index 55e0c86..a3b874b 100644 --- a/credentials/commands/create.go +++ b/credentials/commands/create.go @@ -65,7 +65,7 @@ password.`, check(survey.Ask(mdqs, &ci.MetadataInput)) check(survey.Ask(cqs, &ci)) - key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey)) + key, err := hex.DecodeString(cfg.GetString(clitypes.KeyPrivateKey)) check(err) keypass, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key)) diff --git a/credentials/commands/get.go b/credentials/commands/get.go index a435466..46b05e9 100644 --- a/credentials/commands/get.go +++ b/credentials/commands/get.go @@ -107,7 +107,7 @@ decrypting password.`, fmt.Println("Wrote primary user key to clipboard.") - key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey)) + key, err := hex.DecodeString(cfg.GetString(clitypes.KeyPrivateKey)) check(err) passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), key)