Compare commits

...

2 Commits

Author SHA1 Message Date
Greg 9ead860d17 Add a back button to the passphrase entry page. Clear the clipboard
after 45 seconds.
2019-09-06 11:55:31 -04:00
Greg 59a15849f3 Add password prompt code to GUI. 2019-09-06 11:43:14 -04:00
6 changed files with 149 additions and 27 deletions

View File

@ -1,4 +1,4 @@
//+build !android !linux //+build darwin
package main package main

View File

@ -0,0 +1,38 @@
//+build !darwin
package main
import (
"os"
"os/user"
"path"
"golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/font/sfnt"
)
var (
regular *sfnt.Font
confDir string
)
func init() {
regular, err = sfnt.Parse(goregular.TTF)
if err != nil {
log(Fatal, "Cannot parse default font: ", err)
}
usr, err := user.Current()
if err != nil {
log(Fatal, "Cannot get current user: ", err)
}
confDir = path.Join(usr.HomeDir, ".config/passgo")
if _, err := os.Stat(confDir); os.IsNotExist(err) {
err = os.MkdirAll(confDir, 0700)
if err != nil {
log(Info, "Cannot create configuration directory ", confDir)
log(Fatal, err)
} else {
log(Info, "Configuration directory created")
}
}
}

View File

@ -65,6 +65,7 @@ func main() {
reload = make(chan struct{}) reload = make(chan struct{})
updated = make(chan struct{}) updated = make(chan struct{})
passch = make(chan []byte)
go Updater() go Updater()
log(Info, "Staring event loop") log(Info, "Staring event loop")
@ -80,6 +81,7 @@ var (
store passgo.Store store passgo.Store
reload chan struct{} reload chan struct{}
updated chan struct{} updated chan struct{}
passch chan []byte
black = color.RGBA{A: 0xff, R: 0, G: 0, B: 0} black = color.RGBA{A: 0xff, R: 0, G: 0, B: 0}
white = color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff} white = color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}
gray = color.RGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0} gray = color.RGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0}
@ -284,7 +286,13 @@ func eventLoop() {
Background: darkgray, Background: darkgray,
Alignment: text.Middle, Alignment: text.Middle,
} }
var copiedWhen time.Time cleared := &Overlay{Face: face, Text: "clipboard cleared",
Color: black,
Background: darkgray,
Alignment: text.Middle,
}
overlay := copied
var overlayStart time.Time
updateBtns := func() { updateBtns := func() {
passBtns = passBtns[:0] passBtns = passBtns[:0]
@ -334,6 +342,16 @@ func eventLoop() {
Background: gray, Background: gray,
} }
promptLabel := &text.Label{Face: face, Text: "passphrase"}
promptEd := &text.Editor{Face: face, SingleLine: true}
okBtn := &Button{
Face: face,
Label: "ok",
Alignment: text.End,
Color: black,
Background: gray,
}
anim := &time.Ticker{} anim := &time.Ticker{}
animating := false animating := false
animOn := func() { animOn := func() {
@ -346,8 +364,14 @@ func eventLoop() {
animating = false animating = false
} }
var listPage, confPage, page func() var listPage, confPage, promptPage, page func()
_ = confPage
prompt := func() []byte {
page = promptPage
promptEd.SetText("")
w.Invalidate()
return <-passch
}
listPage = func() { listPage = func() {
cs = flex.Flexible(1.0) cs = flex.Flexible(1.0)
@ -373,12 +397,20 @@ func eventLoop() {
log(Info, "Clicked ", btn.Label) log(Info, "Clicked ", btn.Label)
// don't block UI thread on decryption attempt // don't block UI thread on decryption attempt
go func(name string) { go func(name string) {
//p,err := store.Decrypt(name, prompt) p,err := store.Decrypt(name, prompt)
p, err := store.Decrypt(name) //p, err := store.Decrypt(name)
if err == nil { if err == nil {
passgo.Clip(p) passgo.Clip(p)
copiedWhen = time.Now() overlayStart = time.Now()
overlay = copied
animOn() animOn()
go func() {
time.Sleep(time.Second * 45)
passgo.Clip("")
overlay = cleared
overlayStart = time.Now()
animOn()
}()
} else { } else {
log(Info, "Can't decrypt ", name) log(Info, "Can't decrypt ", name)
log(Info, err) log(Info, err)
@ -395,29 +427,27 @@ func eventLoop() {
page = confPage page = confPage
} }
flex.Layout(c1, c2) flex.Layout(c1, c2)
x := time.Since(copiedWhen).Seconds() x := time.Since(overlayStart).Seconds()
start, end := 1.5, 1.75 start, end := 1.5, 1.75
switch { switch {
case x > start && x < end: case x > start && x < end:
fade := (end - x) / (end - start) fade := (end - x) / (end - start)
copied.Color.R = uint8(float64(black.R) * fade) overlay.Color.R = uint8(float64(black.R) * fade)
copied.Color.G = uint8(float64(black.G) * fade) overlay.Color.G = uint8(float64(black.G) * fade)
copied.Color.B = uint8(float64(black.B) * fade) overlay.Color.B = uint8(float64(black.B) * fade)
copied.Color.A = uint8(float64(black.A) * fade) overlay.Color.A = uint8(float64(black.A) * fade)
copied.Background.R = uint8(float64(darkgray.R) * fade) overlay.Background.R = uint8(float64(darkgray.R) * fade)
copied.Background.G = uint8(float64(darkgray.G) * fade) overlay.Background.G = uint8(float64(darkgray.G) * fade)
copied.Background.B = uint8(float64(darkgray.B) * fade) overlay.Background.B = uint8(float64(darkgray.B) * fade)
copied.Background.A = uint8(float64(darkgray.A) * fade) overlay.Background.A = uint8(float64(darkgray.A) * fade)
fallthrough fallthrough
case x <= start: case x <= start:
cs = margincs cs = margincs
al := layout.Align{Alignment: layout.SE} al := layout.Align{Alignment: layout.SE}
cs = al.Begin(ops, cs) cs = al.Begin(ops, cs)
cs.Width.Min = cs.Width.Max cs.Width.Min = cs.Width.Max
dims = al.End(copied.Layout(c, ops, cs)) dims = al.End(overlay.Layout(c, ops, cs))
case animating: case animating:
copied.Color = black
copied.Background = darkgray
animOff() animOff()
} }
} }
@ -459,6 +489,41 @@ func eventLoop() {
} }
} }
promptPage = func() {
cs = flex.Rigid()
c2 := flex.End(promptLabel.Layout(ops, cs))
cs = flex.Rigid()
c3 := flex.End(promptEd.Layout(c, q, ops, cs))
cs = flex.Rigid()
al := &layout.Align{Alignment: layout.E}
cs = al.Begin(ops, cs)
btnflx := &layout.Flex{Axis: layout.Horizontal}
btnflx.Init(ops, cs)
cs = btnflx.Rigid()
bc1 := btnflx.End(backBtn.Layout(c, q, ops, cs))
cs = btnflx.Rigid()
bc2 := btnflx.End(okBtn.Layout(c, q, ops, cs))
dims = btnflx.Layout(bc1, bc2)
c4 := flex.End(al.End(dims))
flex.Layout(c1, c2, c3, c4)
if okBtn.Clicked() {
log(Info, "Ok")
go func() { // do not block UI thread
passch <-[]byte(promptEd.Text())
}()
w.Invalidate()
page = listPage
}
if backBtn.Clicked() {
log(Info, "Back")
go func() {
passch <- nil // cancel prompt
}()
w.Invalidate()
page = listPage
}
}
page = listPage page = listPage
for { for {

View File

@ -1,4 +1,4 @@
//+build !android !linux //+build darwin
package passgo package passgo

12
impl_non_darwin.go Normal file
View File

@ -0,0 +1,12 @@
//+build !darwin
package passgo
import (
"log"
)
//Clip copies a string to the clipboard
func Clip(x string) {
log(Info,"Clipboard not implemented for this platform")
}

21
main.go
View File

@ -122,15 +122,18 @@ func AskPass(prompts ...func() []byte) openpgp.PromptFunction {
} }
} }
var err error
var passphrase []byte var passphrase []byte
dec := func(p *packet.PrivateKey) { dec := func(p *packet.PrivateKey) error {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
if err = p.Decrypt(passphrase); err == nil { if err := p.Decrypt(passphrase); err == nil {
break return err
} }
passphrase = prompt() passphrase = prompt()
if passphrase == nil {
return fmt.Errorf("Passphrase entry aborted")
}
} }
return fmt.Errorf("Passphrase entry failed")
} }
var ret openpgp.PromptFunction var ret openpgp.PromptFunction
@ -138,16 +141,20 @@ func AskPass(prompts ...func() []byte) openpgp.PromptFunction {
if !symmetric { if !symmetric {
for _, k := range keys { for _, k := range keys {
if p := k.PrivateKey; p != nil && p.Encrypted { if p := k.PrivateKey; p != nil && p.Encrypted {
dec(p) if err := dec(p); err != nil {
return nil, err
}
} }
for _, s := range k.Entity.Subkeys { for _, s := range k.Entity.Subkeys {
if p := s.PrivateKey; p != nil && p.Encrypted { if p := s.PrivateKey; p != nil && p.Encrypted {
dec(p) if err := dec(p); err != nil {
return nil, err
}
} }
} }
} }
} }
return passphrase, err return passphrase, nil
} }
return ret return ret
} }