From f164b3a0af747e9cb8b7a7ce2abaf7e755648641 Mon Sep 17 00:00:00 2001 From: Ian Fijolek Date: Mon, 18 Jan 2021 21:00:56 -0800 Subject: [PATCH] Improve support for YK5 --- info.plist | 441 ++++++++++++++++++++++++++++++++++++++++++++--------- main.go | 118 +++++++++----- ykoath | 2 +- 3 files changed, 455 insertions(+), 106 deletions(-) diff --git a/info.plist b/info.plist index e1f0502..35a3eea 100644 --- a/info.plist +++ b/info.plist @@ -6,11 +6,11 @@ com.iamthefij.alfred-yubico-auth connections - 2D69982A-0DB6-4ABA-812F-C7F2A73650AE + 0149DE47-0232-434D-BB9E-B2C0B419A2E3 destinationuid - 9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD + 55DFD4B6-922A-4259-8625-EC14889FBACD modifiers 0 modifiersubtext @@ -18,6 +18,22 @@ vitoclose + + 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7 + + + destinationuid + 2D69982A-0DB6-4ABA-812F-C7F2A73650AE + modifiers + 0 + modifiersubtext + + vitoclose + + + + 2D69982A-0DB6-4ABA-812F-C7F2A73650AE + destinationuid A8D2CCAC-5CA4-495E-BB62-5C7F596FA157 @@ -30,19 +46,44 @@ vitoclose - - 8486DCAA-AFB7-407D-A0E9-E57E09997B24 - destinationuid - FB0DDF80-FF90-439A-BF3F-6EC58C2AA870 + 0149DE47-0232-434D-BB9E-B2C0B419A2E3 modifiers - 1048576 + 0 modifiersubtext - Paste token + vitoclose + + 55DFD4B6-922A-4259-8625-EC14889FBACD + + + destinationuid + AD82ED59-033E-4860-B371-8128574E2FBC + modifiers + 0 + modifiersubtext + + sourceoutputuid + 94F60406-01FF-4991-A697-2C83147293EB + vitoclose + + + + destinationuid + 506787F2-9A61-492C-8C49-30EE04FB70BC + modifiers + 0 + modifiersubtext + + vitoclose + + + + 8486DCAA-AFB7-407D-A0E9-E57E09997B24 + destinationuid 2D69982A-0DB6-4ABA-812F-C7F2A73650AE @@ -53,6 +94,16 @@ vitoclose + + destinationuid + 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7 + modifiers + 1048576 + modifiersubtext + Paste code + vitoclose + + 9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD @@ -71,7 +122,32 @@ destinationuid - C252A5EC-1AEE-4EF4-864F-67483EAADCFA + DA99BA2E-7234-491D-BD0F-044151FA98E2 + modifiers + 0 + modifiersubtext + + vitoclose + + + + AD82ED59-033E-4860-B371-8128574E2FBC + + + destinationuid + FB0DDF80-FF90-439A-BF3F-6EC58C2AA870 + modifiers + 0 + modifiersubtext + + sourceoutputuid + 94F60406-01FF-4991-A697-2C83147293EB + vitoclose + + + + destinationuid + 9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD modifiers 0 modifiersubtext @@ -106,6 +182,31 @@ + DA99BA2E-7234-491D-BD0F-044151FA98E2 + + + destinationuid + C252A5EC-1AEE-4EF4-864F-67483EAADCFA + modifiers + 0 + modifiersubtext + + sourceoutputuid + 94F60406-01FF-4991-A697-2C83147293EB + vitoclose + + + + destinationuid + 506787F2-9A61-492C-8C49-30EE04FB70BC + modifiers + 0 + modifiersubtext + + vitoclose + + + E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E @@ -146,21 +247,46 @@ config - autopaste - - clipboardtext - {query} - ignoredynamicplaceholders + lastpathcomponent - transient - + onlyshowifquerypopulated + + removeextension + + text + Password key is now stored in your keychain + title + Password saved type - alfred.workflow.output.clipboard + alfred.workflow.output.notification uid - FB0DDF80-FF90-439A-BF3F-6EC58C2AA870 + C252A5EC-1AEE-4EF4-864F-67483EAADCFA version - 3 + 1 + + + config + + concurrently + + escaping + 102 + script + ./alfred-yubico-auth -run-script set-password + scriptargtype + 1 + scriptfile + + type + 0 + + type + alfred.workflow.action.script + uid + A8D2CCAC-5CA4-495E-BB62-5C7F596FA157 + version + 2 config @@ -190,7 +316,7 @@ runningsubtext script - ./alfred-yubico-auth + ./alfred-yubico-auth list scriptargtype 1 scriptfile @@ -211,6 +337,105 @@ version 3 + + config + + conditions + + + inputstring + {var:result} + matchcasesensitive + + matchmode + 0 + matchstring + success + outputlabel + success + uid + 94F60406-01FF-4991-A697-2C83147293EB + + + elselabel + else + + type + alfred.workflow.utility.conditional + uid + DA99BA2E-7234-491D-BD0F-044151FA98E2 + version + 1 + + + config + + conditions + + + inputstring + {var:action} + matchcasesensitive + + matchmode + 0 + matchstring + set-password + outputlabel + Set password + uid + 94F60406-01FF-4991-A697-2C83147293EB + + + elselabel + else + + type + alfred.workflow.utility.conditional + uid + 2D69982A-0DB6-4ABA-812F-C7F2A73650AE + version + 1 + + + config + + argument + {query} + passthroughargument + + variables + + result_action + paste + + + type + alfred.workflow.utility.argument + uid + 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7 + version + 1 + + + config + + autopaste + + clipboardtext + {query} + ignoredynamicplaceholders + + transient + + + type + alfred.workflow.output.clipboard + uid + FB0DDF80-FF90-439A-BF3F-6EC58C2AA870 + version + 3 + config @@ -264,6 +489,61 @@ version 1 + + config + + conditions + + + inputstring + {var:result_action} + matchcasesensitive + + matchmode + 0 + matchstring + paste + outputlabel + paste + uid + 94F60406-01FF-4991-A697-2C83147293EB + + + elselabel + copy + + type + alfred.workflow.utility.conditional + uid + AD82ED59-033E-4860-B371-8128574E2FBC + version + 1 + + + config + + concurrently + + escaping + 102 + script + query=$1 + +./alfred-yubico-auth -run-script "$query" + scriptargtype + 1 + scriptfile + + type + 0 + + type + alfred.workflow.action.script + uid + 0149DE47-0232-434D-BB9E-B2C0B419A2E3 + version + 2 + config @@ -311,15 +591,15 @@ inputstring - {var:action} + {var:result} matchcasesensitive matchmode 0 matchstring - set-password + success outputlabel - Set password + success uid 94F60406-01FF-4991-A697-2C83147293EB @@ -330,51 +610,28 @@ type alfred.workflow.utility.conditional uid - 2D69982A-0DB6-4ABA-812F-C7F2A73650AE + 55DFD4B6-922A-4259-8625-EC14889FBACD version 1 - - config - - concurrently - - escaping - 102 - script - ./alfred-yubico-auth set-password - scriptargtype - 1 - scriptfile - - type - 0 - - type - alfred.workflow.action.script - uid - A8D2CCAC-5CA4-495E-BB62-5C7F596FA157 - version - 2 - config lastpathcomponent onlyshowifquerypopulated - + removeextension text - Password key is now stored in your keychain + {query} title - Password saved + Alfred Yubikey Error type alfred.workflow.output.notification uid - C252A5EC-1AEE-4EF4-864F-67483EAADCFA + 506787F2-9A61-492C-8C49-30EE04FB70BC version 1 @@ -383,75 +640,117 @@ uidata + 0149DE47-0232-434D-BB9E-B2C0B419A2E3 + + xpos + 180 + ypos + 305 + 0718204D-3398-4AEF-A621-DDDE1FC6ED75 xpos - 990 + 1245 ypos - 145 + 225 + + 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7 + + xpos + 260 + ypos + 155 2D69982A-0DB6-4ABA-812F-C7F2A73650AE xpos - 270 + 365 ypos - 245 + 90 + + 506787F2-9A61-492C-8C49-30EE04FB70BC + + xpos + 540 + ypos + 455 + + 55DFD4B6-922A-4259-8625-EC14889FBACD + + xpos + 365 + ypos + 330 8486DCAA-AFB7-407D-A0E9-E57E09997B24 xpos - 120 + 70 ypos - 140 + 65 9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD xpos - 415 + 605 ypos - 235 + 320 A8D2CCAC-5CA4-495E-BB62-5C7F596FA157 xpos - 390 + 515 ypos - 415 + 55 + + AD82ED59-033E-4860-B371-8128574E2FBC + + xpos + 485 + ypos + 285 BFB3A122-52BB-4FF1-B5B3-CECD42A730DB xpos - 810 + 1065 ypos - 145 + 225 C252A5EC-1AEE-4EF4-864F-67483EAADCFA xpos - 595 + 800 ypos - 420 + 45 DA3E9CE8-7C4F-4B09-BFA5-F8CA83297968 xpos - 590 + 780 ypos - 235 + 320 + + DA99BA2E-7234-491D-BD0F-044151FA98E2 + + xpos + 670 + ypos + 75 E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E xpos - 730 + 985 ypos - 175 + 255 FB0DDF80-FF90-439A-BF3F-6EC58C2AA870 xpos - 455 + 605 ypos - 60 + 185 version diff --git a/main.go b/main.go index ec32dad..7fdccbb 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,23 @@ package main import ( + "bytes" + "errors" "flag" + "fmt" "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" + + errIncorrectPassword = errors.New("incorrect password") ) func init() { @@ -28,102 +31,149 @@ func main() { func promptPassword() (string, error) { out, err := util.Run("./password-prompt.js") if err != nil { - return "", err + return "", fmt.Errorf("error reading password from prompt: %w", 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 + return fmt.Errorf("failed reading passphrase: %w", err) } - key := s.DeriveKey(passphrase) - // TODO: test key before storing - err = wf.Keychain.Set(keychainAccount, string(key)) + + err = validatePassphrase(s, passphrase) if err != nil { - slog.Error("failed storing passphrase key in keychain") - return err + return fmt.Errorf("failed validating passphrase: %w", err) } + + err = wf.Keychain.Set(keychainAccount, passphrase) + if err != nil { + return fmt.Errorf("failed storing passphrase in keychain: %w", err) + } + + return nil +} + +func sendResult(result string, args ...string) error { + results := aw.NewArgVars() + + results.Arg(args...) + results.Var("result", result) + + return results.Send() +} + +func validatePassphrase(s *ykoath.Select, passphrase string) error { + key := s.DeriveKey(passphrase) + + // verify password is correct with a validate call + ok, err := oath.Validate(s, key) + if err != nil { + return fmt.Errorf("error in validate: %w", err) + } + + if !ok { + return errIncorrectPassword + } + return nil } func run() { + runScript := flag.Bool("run-script", false, "change output to script output") + wf.Args() flag.Parse() + if *runScript { + wf.Configure(aw.TextErrors(true)) + } + var err error + oath, err = ykoath.New() if err != nil { - slog.Error("failed to iniatialize new oath: %v", err) - wf.FatalError(err) + wf.FatalError(fmt.Errorf("failed to iniatialize new oath: %w", 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) + wf.FatalError(fmt.Errorf("failed to select oath: %w", 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) + wf.FatalError(fmt.Errorf("failed to set password: %w", err)) } + + if err = sendResult("success"); err != nil { + wf.FatalError(fmt.Errorf("failed to send password set result: %w", err)) + } + return } // If required, authenticate with password from keychain if s.Challenge != nil { - key, err := wf.Keychain.Get(keychainAccount) + passphrase, 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") + err = validatePassphrase(s, passphrase) + if err != nil { + wf.FatalError(fmt.Errorf("passphrase failed: %w", err)) } } if flag.Arg(0) == "list" { // List names only names, err := oath.List() - slog.FatalOnErr(err, "failed to list names") + if err != nil { + wf.FatalError(fmt.Errorf("failed to list names: %w", err)) + } + for _, name := range names { slog.Log(name.Name) wf.NewItem(name.Name). + Icon(aw.IconAccount). + Subtitle("Copy to clipboard"). + Arg(name.Name). Valid(true) } } else { - // Default execution is to calculate all codes and return them in list - creds, err := oath.CalculateAll() + name := flag.Arg(0) + + code, err := oath.CalculateOne(name) if err != nil { - slog.Error("failed to calculate all") - wf.FatalError(err) + // TODO: Check for error "requires-auth" and notify touch + wf.FatalError(fmt.Errorf("failed to generate code: %w", 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) + slog.Log(code) + + if err = sendResult("success", code); err != nil { + wf.FatalError(fmt.Errorf("failed to send code: %w", err)) } } - wf.SendFeedback() + + if !*runScript { + wf.SendFeedback() + } } diff --git a/ykoath b/ykoath index 0bb0699..fd081cb 160000 --- a/ykoath +++ b/ykoath @@ -1 +1 @@ -Subproject commit 0bb0699e51d3672572cc7869397eeff871bdf45e +Subproject commit fd081cb213d030585bfdd03305e03bff4d6e7a09