130 lines
2.5 KiB
Go
130 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
|
|
"git.iamthefij.com/iamthefij/slog"
|
|
aw "github.com/deanishe/awgo"
|
|
"github.com/deanishe/awgo/util"
|
|
"github.com/yawn/ykoath"
|
|
|
|
"bytes"
|
|
)
|
|
|
|
var (
|
|
wf *aw.Workflow
|
|
oath *ykoath.OATH
|
|
keychainAccount = "yubico-auth-creds"
|
|
)
|
|
|
|
func init() {
|
|
wf = aw.New()
|
|
}
|
|
|
|
func main() {
|
|
wf.Run(run)
|
|
}
|
|
|
|
func promptPassword() (string, error) {
|
|
out, err := util.Run("./password-prompt.js")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
out = bytes.TrimRight(out, "\n")
|
|
return string(out), nil
|
|
}
|
|
|
|
func setPassword(s *ykoath.Select) error {
|
|
passphrase, err := promptPassword()
|
|
if err != nil {
|
|
slog.Error("failed reading passphrase")
|
|
return err
|
|
}
|
|
key := s.DeriveKey(passphrase)
|
|
// TODO: test key before storing
|
|
err = wf.Keychain.Set(keychainAccount, string(key))
|
|
if err != nil {
|
|
slog.Error("failed storing passphrase key in keychain")
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func run() {
|
|
wf.Args()
|
|
flag.Parse()
|
|
|
|
var err error
|
|
oath, err = ykoath.New()
|
|
if err != nil {
|
|
slog.Error("failed to iniatialize new oath: %v", err)
|
|
wf.FatalError(err)
|
|
}
|
|
defer oath.Close()
|
|
oath.Debug = slog.Debug
|
|
|
|
// Select oath to begin
|
|
s, err := oath.Select()
|
|
if err != nil {
|
|
slog.Error("failed to select oath: %v", err)
|
|
wf.FatalError(err)
|
|
}
|
|
|
|
// Check to see if we are trying to set a password
|
|
if flag.Arg(0) == "set-password" {
|
|
err = setPassword(s)
|
|
if err != nil {
|
|
wf.FatalError(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// If required, authenticate with password from keychain
|
|
if s.Challenge != nil {
|
|
key, err := wf.Keychain.Get(keychainAccount)
|
|
if err != nil {
|
|
slog.Error("no key found in keychain but password is required")
|
|
wf.NewWarningItem("No password set", "↵ to set password").
|
|
Var("action", "set-password").
|
|
Valid(true)
|
|
wf.SendFeedback()
|
|
return
|
|
}
|
|
|
|
ok, err := oath.Validate(s, []byte(key))
|
|
slog.FatalOnErr(err, "validation failed")
|
|
if !ok {
|
|
panic("could not validate")
|
|
}
|
|
}
|
|
|
|
if flag.Arg(0) == "list" {
|
|
// List names only
|
|
names, err := oath.List()
|
|
slog.FatalOnErr(err, "failed to list names")
|
|
for _, name := range names {
|
|
slog.Log(name.Name)
|
|
wf.NewItem(name.Name).
|
|
Valid(true)
|
|
}
|
|
} else {
|
|
// Default execution is to calculate all codes and return them in list
|
|
creds, err := oath.CalculateAll()
|
|
if err != nil {
|
|
slog.Error("failed to calculate all")
|
|
wf.FatalError(err)
|
|
}
|
|
|
|
for cred, code := range creds {
|
|
slog.Log(cred)
|
|
wf.NewItem(cred).
|
|
Icon(aw.IconAccount).
|
|
Subtitle("Copy to clipboard").
|
|
Arg(code).
|
|
Copytext(code).
|
|
Valid(true)
|
|
}
|
|
}
|
|
wf.SendFeedback()
|
|
}
|