mirror of
https://github.com/mitchell/selfpass.git
synced 2025-12-14 13:27:21 +00:00
Separate cli into its own module; rename it to sp
This commit is contained in:
parent
0e071f0f89
commit
024017338e
25 changed files with 405 additions and 392 deletions
159
sp/repositories/config.go
Normal file
159
sp/repositories/config.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
|
||||
"github.com/mitchell/selfpass/sp/crypto"
|
||||
"github.com/mitchell/selfpass/sp/types"
|
||||
)
|
||||
|
||||
func NewConfigManager(cfgFile *string) *ConfigManager {
|
||||
return &ConfigManager{
|
||||
cfgFile: cfgFile,
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigManager struct {
|
||||
masterpass string
|
||||
cfgFile *string
|
||||
v *viper.Viper
|
||||
}
|
||||
|
||||
func (mgr *ConfigManager) SetMasterpass(masterpass string) {
|
||||
mgr.masterpass = masterpass
|
||||
}
|
||||
|
||||
func (mgr *ConfigManager) OpenConfig() (output string, v *viper.Viper, err error) {
|
||||
if mgr.masterpass != "" {
|
||||
return mgr.masterpass, mgr.v, nil
|
||||
}
|
||||
cfg := *mgr.cfgFile
|
||||
|
||||
mgr.v = viper.New()
|
||||
mgr.v.SetConfigType("toml")
|
||||
|
||||
if cfg == "" {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return output, nil, err
|
||||
}
|
||||
|
||||
cfg = home + "/.sp.toml"
|
||||
}
|
||||
|
||||
mgr.v.SetConfigFile(cfg)
|
||||
mgr.cfgFile = &cfg
|
||||
|
||||
var contents []byte
|
||||
var configDecrypted 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 = decryptConfig(mgr.masterpass, cfg)
|
||||
if err != nil && err == errConfigDecrypted {
|
||||
configDecrypted = true
|
||||
} else if err != nil {
|
||||
return output, nil, err
|
||||
}
|
||||
|
||||
if err = mgr.v.ReadConfig(bytes.NewBuffer(contents)); err != nil && strings.HasPrefix(err.Error(), "While parsing config") {
|
||||
return output, nil, fmt.Errorf("incorrect master password")
|
||||
} else if err != nil {
|
||||
return output, nil, err
|
||||
}
|
||||
|
||||
if configDecrypted {
|
||||
fmt.Println("Config wasn't encrypted, or has been compromised.")
|
||||
|
||||
if err = mgr.WriteConfig(); err != nil {
|
||||
return output, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return mgr.masterpass, mgr.v, nil
|
||||
}
|
||||
|
||||
var errConfigDecrypted = errors.New("config is decrypted")
|
||||
|
||||
func decryptConfig(masterpass string, cfgFile string) (contents []byte, err error) {
|
||||
contents, err = ioutil.ReadFile(cfgFile)
|
||||
if err != nil {
|
||||
return contents, err
|
||||
}
|
||||
|
||||
if string(contents[:len(types.KeyPrivateKey)]) == types.KeyPrivateKey {
|
||||
return contents, errConfigDecrypted
|
||||
}
|
||||
|
||||
salt := contents[:saltSize]
|
||||
contents = contents[saltSize:]
|
||||
|
||||
passkey := crypto.GeneratePBKDF2Key([]byte(masterpass), salt)
|
||||
|
||||
plaintext, err := crypto.GCMDecrypt(passkey, contents)
|
||||
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 err
|
||||
}
|
||||
|
||||
salt := make([]byte, saltSize)
|
||||
_, err = rand.Read(salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keypass := crypto.GeneratePBKDF2Key([]byte(mgr.masterpass), salt)
|
||||
|
||||
contents, err = crypto.GCMEncrypt(keypass, contents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contents = append(salt, contents...)
|
||||
|
||||
err = ioutil.WriteFile(mgr.v.ConfigFileUsed(), contents, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const saltSize = 16
|
||||
Loading…
Add table
Add a link
Reference in a new issue