Compatibility with gpg 2.0 -- call out to gpg commands (on Darwin)
if go openpgp is not compatibile with the local gpg installation.
This commit is contained in:
parent
44cab96fff
commit
09859ca0a9
|
@ -49,7 +49,7 @@ func main() {
|
||||||
log(Info, " StoreDir = ", store.Dir)
|
log(Info, " StoreDir = ", store.Dir)
|
||||||
err = passgo.GetStore(&store)
|
err = passgo.GetStore(&store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Fatal, err)
|
log(Info, err)
|
||||||
}
|
}
|
||||||
if Config.ClearDelay == 0 {
|
if Config.ClearDelay == 0 {
|
||||||
Config.ClearDelay = 45
|
Config.ClearDelay = 45
|
||||||
|
@ -62,8 +62,10 @@ func main() {
|
||||||
|
|
||||||
reload = make(chan struct{})
|
reload = make(chan struct{})
|
||||||
updated = make(chan struct{})
|
updated = make(chan struct{})
|
||||||
|
chdir = make(chan struct{})
|
||||||
passch = make(chan []byte)
|
passch = make(chan []byte)
|
||||||
|
|
||||||
|
passgo.Identities()
|
||||||
go Updater()
|
go Updater()
|
||||||
log(Info, "Staring event loop")
|
log(Info, "Staring event loop")
|
||||||
go eventLoop()
|
go eventLoop()
|
||||||
|
@ -78,6 +80,7 @@ var (
|
||||||
store passgo.Store
|
store passgo.Store
|
||||||
reload chan struct{}
|
reload chan struct{}
|
||||||
updated chan struct{}
|
updated chan struct{}
|
||||||
|
chdir chan struct{}
|
||||||
passch chan []byte
|
passch chan []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -98,7 +101,8 @@ func Updater() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Fatal, err)
|
log(Fatal, err)
|
||||||
}
|
}
|
||||||
watcher.Add(store.Dir)
|
dir := store.Dir
|
||||||
|
watcher.Add(dir)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-reload:
|
case <-reload:
|
||||||
|
@ -107,6 +111,13 @@ func Updater() {
|
||||||
update()
|
update()
|
||||||
case e := <-watcher.Errors:
|
case e := <-watcher.Errors:
|
||||||
log(Info, "Watcher error: ", e)
|
log(Info, "Watcher error: ", e)
|
||||||
|
case <-chdir:
|
||||||
|
err := watcher.Remove(dir)
|
||||||
|
if err != nil {
|
||||||
|
log(Info, "Error removing watcher: ", err)
|
||||||
|
}
|
||||||
|
watcher.Add(store.Dir)
|
||||||
|
update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,10 +162,10 @@ func eventLoop() {
|
||||||
margin := layout.UniformInset(ui.Dp(10))
|
margin := layout.UniformInset(ui.Dp(10))
|
||||||
|
|
||||||
title := &text.Label{Face: face, Text: "passgo"}
|
title := &text.Label{Face: face, Text: "passgo"}
|
||||||
dotsbtn := &Button{
|
dotsBtn := &Button{
|
||||||
Face: face,
|
Face: face,
|
||||||
Label: "\xe2\x8b\xae",
|
Label: "\xe2\x8b\xae",
|
||||||
Alignment: text.End,
|
Alignment: text.Middle,
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
|
@ -200,6 +211,30 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
updateBtns()
|
updateBtns()
|
||||||
|
|
||||||
|
idBtns := make([]*Button, 0)
|
||||||
|
|
||||||
|
updateIdBtns := func() {
|
||||||
|
ids, err := passgo.Identities()
|
||||||
|
if err != nil {
|
||||||
|
log(Info, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i,n := range ids {
|
||||||
|
if i >= len(idBtns) {
|
||||||
|
idBtns = append(idBtns, &Button{
|
||||||
|
Face: face,
|
||||||
|
Label: n,
|
||||||
|
Alignment: text.End,
|
||||||
|
Color: black,
|
||||||
|
Background: gray,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
idBtns[i].Label = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idBtns = idBtns[:len(ids)]
|
||||||
|
}
|
||||||
|
|
||||||
confBtn := &Button{
|
confBtn := &Button{
|
||||||
Face: face,
|
Face: face,
|
||||||
Label: "configure",
|
Label: "configure",
|
||||||
|
@ -225,6 +260,14 @@ func eventLoop() {
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
|
confirmLabel := &text.Label{Face: face, Text: "Password exists. Overwrite?"}
|
||||||
|
yesBtn := &Button{
|
||||||
|
Face: face,
|
||||||
|
Label: "yes",
|
||||||
|
Alignment: text.End,
|
||||||
|
Color: black,
|
||||||
|
Background: gray,
|
||||||
|
}
|
||||||
|
|
||||||
promptLabel := &text.Label{Face: face, Text: "passphrase"}
|
promptLabel := &text.Label{Face: face, Text: "passphrase"}
|
||||||
promptEd := &text.Editor{Face: face, SingleLine: true, Submit: true}
|
promptEd := &text.Editor{Face: face, SingleLine: true, Submit: true}
|
||||||
|
@ -235,6 +278,22 @@ func eventLoop() {
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
|
plusBtn := &Button{
|
||||||
|
Face: face,
|
||||||
|
Label: "+",
|
||||||
|
Alignment: text.Middle,
|
||||||
|
Color: black,
|
||||||
|
Background: gray,
|
||||||
|
}
|
||||||
|
|
||||||
|
insertLabel := &text.Label{Face: face, Text: "Insert"}
|
||||||
|
passnameLabel := &text.Label{Face: face, Text: "password name:"}
|
||||||
|
passnameEd := &text.Editor{Face: face, SingleLine: true, Submit: true}
|
||||||
|
passvalLabel := &text.Label{Face: face, Text: "password value:"}
|
||||||
|
passvalEd := &text.Editor{Face: face, SingleLine: true, Submit: true}
|
||||||
|
|
||||||
|
noidLabel := &text.Label{Face: face, Text: "No GPG ids available. Please create a private key"}
|
||||||
|
idLabel := &text.Label{Face: face, Text: "Select ID"}
|
||||||
|
|
||||||
anim := &time.Ticker{}
|
anim := &time.Ticker{}
|
||||||
animating := false
|
animating := false
|
||||||
|
@ -250,7 +309,7 @@ func eventLoop() {
|
||||||
animating = false
|
animating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var listPage, confPage, promptPage, page func()
|
var listPage, idPage, insertPage, confirmPage, confPage, promptPage, page func()
|
||||||
|
|
||||||
prompt := func() []byte {
|
prompt := func() []byte {
|
||||||
page = promptPage
|
page = promptPage
|
||||||
|
@ -271,14 +330,19 @@ func eventLoop() {
|
||||||
key.HideInputOp{}.Add(ops)
|
key.HideInputOp{}.Add(ops)
|
||||||
}
|
}
|
||||||
var c2 layout.FlexChild
|
var c2 layout.FlexChild
|
||||||
if len(passBtns) == 0 {
|
switch {
|
||||||
|
case store.Empty:
|
||||||
c2 = flex.End(confBtn.Layout(c, q, ops, cs))
|
c2 = flex.End(confBtn.Layout(c, q, ops, cs))
|
||||||
if confBtn.Clicked() {
|
if confBtn.Clicked() {
|
||||||
log(Info, "Configure")
|
log(Info, "Configure")
|
||||||
w.Invalidate()
|
w.Invalidate()
|
||||||
page = confPage
|
page = confPage
|
||||||
}
|
}
|
||||||
} else {
|
case store.Id == "":
|
||||||
|
c2 = flex.End(idLabel.Layout(ops, cs))
|
||||||
|
w.Invalidate()
|
||||||
|
page = idPage
|
||||||
|
default:
|
||||||
for lst.Init(c, q, ops, cs, len(passBtns)); lst.More(); lst.Next() {
|
for lst.Init(c, q, ops, cs, len(passBtns)); lst.More(); lst.Next() {
|
||||||
i := lst.Index()
|
i := lst.Index()
|
||||||
btn := passBtns[i]
|
btn := passBtns[i]
|
||||||
|
@ -316,11 +380,6 @@ func eventLoop() {
|
||||||
c2 = flex.End(lst.Layout())
|
c2 = flex.End(lst.Layout())
|
||||||
}
|
}
|
||||||
mux.Unlock()
|
mux.Unlock()
|
||||||
if dotsbtn.Clicked() {
|
|
||||||
log(Info, "Configure")
|
|
||||||
w.Invalidate()
|
|
||||||
page = confPage
|
|
||||||
}
|
|
||||||
flex.Layout(c1, c2)
|
flex.Layout(c1, c2)
|
||||||
x := time.Since(overlayStart).Seconds()
|
x := time.Since(overlayStart).Seconds()
|
||||||
if x >= fade1b && x < start2 && animating {
|
if x >= fade1b && x < start2 && animating {
|
||||||
|
@ -372,6 +431,99 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idPage = func() {
|
||||||
|
if !animating {
|
||||||
|
animOn()
|
||||||
|
}
|
||||||
|
cs = flex.Rigid()
|
||||||
|
c2 := flex.End(idLabel.Layout(ops, cs))
|
||||||
|
var c3 layout.FlexChild
|
||||||
|
//if len(idBtns) == 0 {
|
||||||
|
updateIdBtns()
|
||||||
|
//}
|
||||||
|
cs = flex.Rigid()
|
||||||
|
if len(idBtns) == 0 { // still zero after update
|
||||||
|
c3 = flex.End(noidLabel.Layout(ops, cs))
|
||||||
|
} else {
|
||||||
|
for lst.Init(c, q, ops, cs, len(idBtns)); lst.More(); lst.Next() {
|
||||||
|
lst.End(idBtns[lst.Index()].Layout(c, q, ops, lst.Constraints()))
|
||||||
|
}
|
||||||
|
c3 = flex.End(lst.Layout())
|
||||||
|
for _, btn := range idBtns {
|
||||||
|
if btn.Clicked() {
|
||||||
|
log(Info, "ID selected: ", btn.Label)
|
||||||
|
store.SetID(btn.Label)
|
||||||
|
w.Invalidate()
|
||||||
|
animOff()
|
||||||
|
page = listPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flex.Layout(c1, c2, c3)
|
||||||
|
}
|
||||||
|
|
||||||
|
var insName, insValue string
|
||||||
|
|
||||||
|
insertPage = func() {
|
||||||
|
cs = flex.Rigid()
|
||||||
|
c2 := flex.End(insertLabel.Layout(ops, cs))
|
||||||
|
|
||||||
|
c3 := flex.End(passnameLabel.Layout(ops, flex.Rigid()))
|
||||||
|
c4 := flex.End(passnameEd.Layout(c, q, ops, flex.Rigid()))
|
||||||
|
c5 := flex.End(passvalLabel.Layout(ops, flex.Rigid()))
|
||||||
|
c6 := flex.End(passvalEd.Layout(c, q, ops, flex.Rigid()))
|
||||||
|
cs = flex.Rigid()
|
||||||
|
al := &layout.Align{Alignment: layout.E}
|
||||||
|
btnflx := &layout.Flex{Axis: layout.Horizontal}
|
||||||
|
btnflx.Init(ops, al.Begin(ops, cs))
|
||||||
|
bc1 := btnflx.End(backBtn.Layout(c, q, ops, btnflx.Rigid()))
|
||||||
|
bc2 := btnflx.End(saveBtn.Layout(c, q, ops, btnflx.Rigid()))
|
||||||
|
c7 := flex.End(al.End(btnflx.Layout(bc1, bc2)))
|
||||||
|
flex.Layout(c1, c2, c3, c4, c5, c6, c7)
|
||||||
|
|
||||||
|
if backBtn.Clicked() {
|
||||||
|
w.Invalidate()
|
||||||
|
page = listPage
|
||||||
|
}
|
||||||
|
if saveBtn.Clicked() {
|
||||||
|
w.Invalidate()
|
||||||
|
page = listPage
|
||||||
|
insName = passnameEd.Text()
|
||||||
|
insValue = passvalEd.Text()
|
||||||
|
for _, n := range pathnames {
|
||||||
|
if insName == n {
|
||||||
|
log(Info,"Password exists")
|
||||||
|
page = confirmPage
|
||||||
|
w.Invalidate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store.Insert(passnameEd.Text(), passvalEd.Text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmPage = func() {
|
||||||
|
cs = flex.Rigid()
|
||||||
|
c2 := flex.End(confirmLabel.Layout(ops, cs))
|
||||||
|
al := &layout.Align{Alignment: layout.E}
|
||||||
|
btnflx := &layout.Flex{Axis: layout.Horizontal}
|
||||||
|
btnflx.Init(ops, al.Begin(ops, flex.Rigid()))
|
||||||
|
bc1 := btnflx.End(backBtn.Layout(c, q, ops, btnflx.Rigid()))
|
||||||
|
bc2 := btnflx.End(yesBtn.Layout(c, q, ops, btnflx.Rigid()))
|
||||||
|
c3 := flex.End(al.End(btnflx.Layout(bc1, bc2)))
|
||||||
|
flex.Layout(c1, c2, c3)
|
||||||
|
|
||||||
|
if backBtn.Clicked() {
|
||||||
|
w.Invalidate()
|
||||||
|
page = insertPage
|
||||||
|
}
|
||||||
|
if yesBtn.Clicked() {
|
||||||
|
w.Invalidate()
|
||||||
|
page = listPage
|
||||||
|
store.Insert(insName, insValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
confPage = func() {
|
confPage = func() {
|
||||||
cs = flex.Rigid()
|
cs = flex.Rigid()
|
||||||
c2 := flex.End(storeDirLabel.Layout(ops, cs))
|
c2 := flex.End(storeDirLabel.Layout(ops, cs))
|
||||||
|
@ -399,10 +551,12 @@ func eventLoop() {
|
||||||
log(Info, "Save")
|
log(Info, "Save")
|
||||||
go func() { // do not block UI thread
|
go func() { // do not block UI thread
|
||||||
store.Dir = storeDirEd.Text()
|
store.Dir = storeDirEd.Text()
|
||||||
|
store.Id = ""
|
||||||
passgo.GetStore(&store)
|
passgo.GetStore(&store)
|
||||||
Config.StoreDir = store.Dir
|
Config.StoreDir = store.Dir
|
||||||
|
store.Mkdir()
|
||||||
saveConf()
|
saveConf()
|
||||||
reload <- struct{}{}
|
chdir <- struct{}{}
|
||||||
}()
|
}()
|
||||||
w.Invalidate()
|
w.Invalidate()
|
||||||
page = listPage
|
page = listPage
|
||||||
|
@ -477,15 +631,29 @@ func eventLoop() {
|
||||||
cs = flex.Rigid()
|
cs = flex.Rigid()
|
||||||
titleflex.Init(ops, cs)
|
titleflex.Init(ops, cs)
|
||||||
cs = titleflex.Rigid()
|
cs = titleflex.Rigid()
|
||||||
ct2 := titleflex.End(dotsbtn.Layout(c, q, ops, cs))
|
ct2 := titleflex.End(plusBtn.Layout(c, q, ops, cs))
|
||||||
|
cs = titleflex.Rigid()
|
||||||
|
ct3 := titleflex.End(dotsBtn.Layout(c, q, ops, cs))
|
||||||
cs = titleflex.Flexible(1.0)
|
cs = titleflex.Flexible(1.0)
|
||||||
cs.Width.Min = cs.Width.Max
|
cs.Width.Min = cs.Width.Max
|
||||||
ct1 := titleflex.End(title.Layout(ops, cs))
|
ct1 := titleflex.End(title.Layout(ops, cs))
|
||||||
c1 = flex.End(titleflex.Layout(ct1, ct2))
|
c1 = flex.End(titleflex.Layout(ct1, ct2, ct3))
|
||||||
|
|
||||||
page()
|
page()
|
||||||
|
|
||||||
margin.End(dims)
|
margin.End(dims)
|
||||||
|
|
||||||
|
if dotsBtn.Clicked() {
|
||||||
|
log(Info, "Configure")
|
||||||
|
w.Invalidate()
|
||||||
|
page = confPage
|
||||||
|
}
|
||||||
|
if plusBtn.Clicked() {
|
||||||
|
log(Info, "Plus")
|
||||||
|
w.Invalidate()
|
||||||
|
insName, insValue = "", ""
|
||||||
|
page = insertPage
|
||||||
|
}
|
||||||
w.Update(ops)
|
w.Update(ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ func (b *Button) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.Const
|
||||||
Alignment: b.Alignment,
|
Alignment: b.Alignment,
|
||||||
}
|
}
|
||||||
ins := layout.UniformInset(ui.Dp(4))
|
ins := layout.UniformInset(ui.Dp(4))
|
||||||
paint.ColorOp{Color: b.Color}.Add(ops)
|
//paint.ColorOp{Color: b.Color}.Add(ops)
|
||||||
dims = ins.End(l.Layout(ops, ins.Begin(c, ops, cs)))
|
dims = ins.End(l.Layout(ops, ins.Begin(c, ops, cs)))
|
||||||
pointer.RectAreaOp{image.Rect(0, 0, dims.Size.X, dims.Size.Y)}.Add(ops)
|
pointer.RectAreaOp{image.Rect(0, 0, dims.Size.X, dims.Size.Y)}.Add(ops)
|
||||||
b.Click.Add(ops)
|
b.Click.Add(ops)
|
||||||
|
|
|
@ -10,9 +10,75 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s *Store) gpgDecrypt(name string) (string, error) {
|
||||||
|
fmt.Println("calling gpg -d")
|
||||||
|
file := path.Join(s.Dir, strings.Join([]string{name, ".gpg"}, ""))
|
||||||
|
if _, err := os.Stat(file); os.IsNotExist(err) {
|
||||||
|
return "", fmt.Errorf("Not in password store.")
|
||||||
|
}
|
||||||
|
output, err := exec.Command("gpg", "-d", file).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error running gpg: %s", err)
|
||||||
|
}
|
||||||
|
return string(output[:len(output)-1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) gpgEncrypt(pw string) ([]byte, error) {
|
||||||
|
if s.Id == "" {
|
||||||
|
return nil, fmt.Errorf("No ID")
|
||||||
|
}
|
||||||
|
fmt.Printf("Calling gpg -e -r %s\n", s.Id)
|
||||||
|
cmd := exec.Command("gpg", "-e", "-r", s.Id)
|
||||||
|
cmd.Stdin = strings.NewReader(pw + "\n")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error running GPG: %s\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Println("success")
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
idMux sync.Mutex
|
||||||
|
cachedIdentities []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func gpgIdentities() ([]string, error) {
|
||||||
|
idMux.Lock()
|
||||||
|
defer idMux.Unlock()
|
||||||
|
if cachedIdentities != nil {
|
||||||
|
ret := make([]string,len(cachedIdentities))
|
||||||
|
copy(ret, cachedIdentities)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
ret := make([]string,0)
|
||||||
|
fmt.Println("calling gpg")
|
||||||
|
output, err := exec.Command("gpg", "--list-secret-keys", "--with-colons").Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error running gpg: %s", err)
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(bytes.NewBuffer(output))
|
||||||
|
for scanner.Scan() {
|
||||||
|
fs := strings.Split(scanner.Text(),":")
|
||||||
|
if fs[0] == "uid" {
|
||||||
|
fmt.Printf("%s: %s\n",fs[0], fs[9])
|
||||||
|
ret = append(ret, fs[9])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ret) > 0 {
|
||||||
|
cachedIdentities = ret
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
func setAgentInfo() {
|
func setAgentInfo() {
|
||||||
// get UID of current user
|
// get UID of current user
|
||||||
usr, err := user.Current()
|
usr, err := user.Current()
|
||||||
|
@ -77,10 +143,33 @@ func setAgentInfo() {
|
||||||
|
|
||||||
// gpg-agent is running, so use GPGPrompt as password prompt
|
// gpg-agent is running, so use GPGPrompt as password prompt
|
||||||
ask = GPGPrompt
|
ask = GPGPrompt
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
setAgentInfo()
|
setAgentInfo()
|
||||||
|
go func() {
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Cannot create filesystem watcher: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Cannot get current user: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
watcher.Add(path.Join(u.HomeDir, ".gnupg"))
|
||||||
|
fmt.Println("Watching ", path.Join(u.HomeDir, ".gnupg"))
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-watcher.Events:
|
||||||
|
idMux.Lock()
|
||||||
|
cachedIdentities = nil
|
||||||
|
idMux.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Clip copies a string to the clipboard
|
//Clip copies a string to the clipboard
|
||||||
|
@ -90,3 +179,4 @@ func Clip(x string) {
|
||||||
cmd.Stdin = b
|
cmd.Stdin = b
|
||||||
cmd.Run()
|
cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
105
main.go
105
main.go
|
@ -12,6 +12,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jcmdev0/gpgagent"
|
"github.com/jcmdev0/gpgagent"
|
||||||
|
|
||||||
|
@ -23,15 +24,21 @@ import (
|
||||||
var (
|
var (
|
||||||
basename *regexp.Regexp
|
basename *regexp.Regexp
|
||||||
ask openpgp.PromptFunction
|
ask openpgp.PromptFunction
|
||||||
|
Keyring openpgp.KeyRing
|
||||||
|
krTime time.Time
|
||||||
|
useGPG bool
|
||||||
|
homeDir string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
useGPG = true // default unless we can get the Go openpgp code to work
|
||||||
basename = regexp.MustCompile(".gpg$")
|
basename = regexp.MustCompile(".gpg$")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
Dir string
|
Dir string
|
||||||
keyring openpgp.KeyRing
|
Id string
|
||||||
|
Empty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStore(store *Store) error {
|
func GetStore(store *Store) error {
|
||||||
|
@ -42,20 +49,70 @@ func GetStore(store *Store) error {
|
||||||
if store.Dir == "" {
|
if store.Dir == "" {
|
||||||
store.Dir = path.Join(u.HomeDir, ".password-store")
|
store.Dir = path.Join(u.HomeDir, ".password-store")
|
||||||
}
|
}
|
||||||
|
if _, err := os.Stat(store.Dir); os.IsNotExist(err) {
|
||||||
|
store.Empty = true
|
||||||
|
}
|
||||||
|
id, err := ioutil.ReadFile(path.Join(store.Dir, ".gpg-id"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't read .gpg-id: %s", err)
|
||||||
|
}
|
||||||
|
if id[len(id)-1] == '\n' {
|
||||||
|
id = id[:len(id)-1]
|
||||||
|
}
|
||||||
|
store.Id = string(id)
|
||||||
|
return getKeyring()
|
||||||
|
}
|
||||||
|
|
||||||
fd, err := os.Open(path.Join(u.HomeDir, ".gnupg/secring.gpg"))
|
func getKeyring() error {
|
||||||
|
u, err := user.Current()
|
||||||
|
homeDir = u.HomeDir
|
||||||
|
krfile := path.Join(homeDir, ".gnupg/secring.gpg")
|
||||||
|
if fi, err := os.Stat(krfile); os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
if krTime.Sub(fi.ModTime()) > 0 { // already loaded
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Loading secring.gpg")
|
||||||
|
fd, err := os.Open(krfile)
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't open keyring file")
|
return nil
|
||||||
}
|
}
|
||||||
kr, err := openpgp.ReadKeyRing(fd)
|
kr, err := openpgp.ReadKeyRing(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't open gnupg keyring.")
|
//return fmt.Errorf("Can't open gnupg keyring.")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
store.keyring = kr
|
useGPG = false
|
||||||
|
Keyring = kr
|
||||||
|
krTime = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) Mkdir() error {
|
||||||
|
err := os.MkdirAll(s.Dir, 0700)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Cannot create store directory.")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Empty = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) SetID(id string) error {
|
||||||
|
s.Id = id
|
||||||
|
if _, err := os.Stat(s.Dir); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("store directory does not exist.\n")
|
||||||
|
err := s.Mkdir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(path.Join(s.Dir, ".gpg-id"), []byte(id + "\n"), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
type caseInsensitive []os.FileInfo
|
type caseInsensitive []os.FileInfo
|
||||||
|
|
||||||
func (fs caseInsensitive) Len() int { return len(fs) }
|
func (fs caseInsensitive) Len() int { return len(fs) }
|
||||||
|
@ -192,6 +249,9 @@ func GPGPrompt(keys []openpgp.Key, symmetric bool) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Decrypt(name string, prompts ...func() []byte) (string, error) {
|
func (s *Store) Decrypt(name string, prompts ...func() []byte) (string, error) {
|
||||||
|
if useGPG {
|
||||||
|
return s.gpgDecrypt(name)
|
||||||
|
}
|
||||||
if ask == nil {
|
if ask == nil {
|
||||||
ask = AskPass(prompts...)
|
ask = AskPass(prompts...)
|
||||||
}
|
}
|
||||||
|
@ -217,7 +277,7 @@ func (s *Store) Decrypt(name string, prompts ...func() []byte) (string, error) {
|
||||||
}
|
}
|
||||||
reader = fd
|
reader = fd
|
||||||
}
|
}
|
||||||
md, err := openpgp.ReadMessage(reader, s.keyring, ask, nil)
|
md, err := openpgp.ReadMessage(reader, Keyring, ask, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading message")
|
fmt.Println("Error reading message")
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -226,5 +286,36 @@ func (s *Store) Decrypt(name string, prompts ...func() []byte) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return string(ra), nil
|
if len(ra) > 1 {
|
||||||
|
return string(ra[:len(ra)-1]), nil
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("Password is empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Identities() ([]string, error) {
|
||||||
|
getKeyring()
|
||||||
|
if useGPG {
|
||||||
|
return gpgIdentities()
|
||||||
|
}
|
||||||
|
ret := make([]string, 0)
|
||||||
|
for _, k := range Keyring.DecryptionKeys() {
|
||||||
|
for n, _ := range k.Entity.Identities {
|
||||||
|
ret = append(ret, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Insert(name, value string) error {
|
||||||
|
var enc []byte
|
||||||
|
var err error
|
||||||
|
//if !useGo { // golang openpgp code not implemented yet
|
||||||
|
fmt.Println("Calling gpgEncrypt")
|
||||||
|
enc, err = s.gpgEncrypt(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
return ioutil.WriteFile(path.Join(s.Dir, name + ".gpg"), enc, 0644)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user