summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2017-12-28 05:13:19 +0000
committerMike Crute <mike@crute.us>2017-12-28 05:13:19 +0000
commit6f7a7647c7716377ee4d4e5afee36dd85fc01031 (patch)
tree59b7dfa813313e4f4b041e336d8f45f8841e6e8c
parentb96e751c8944c1b63bf78033c76892c72abad432 (diff)
downloadgo-1password-6f7a7647c7716377ee4d4e5afee36dd85fc01031.tar.bz2
go-1password-6f7a7647c7716377ee4d4e5afee36dd85fc01031.tar.xz
go-1password-6f7a7647c7716377ee4d4e5afee36dd85fc01031.zip
Add search, somewhatHEADmaster
-rw-r--r--main.go167
1 files changed, 141 insertions, 26 deletions
diff --git a/main.go b/main.go
index 88cca34..6664cd5 100644
--- a/main.go
+++ b/main.go
@@ -13,8 +13,11 @@ import (
13 "errors" 13 "errors"
14 "fmt" 14 "fmt"
15 "io/ioutil" 15 "io/ioutil"
16 "net/url"
17 "os"
16 "os/user" 18 "os/user"
17 "path" 19 "path"
20 "regexp"
18 "strings" 21 "strings"
19 "syscall" 22 "syscall"
20 "time" 23 "time"
@@ -82,6 +85,26 @@ func NewKeyPBKDF2(pass []byte, p *OPProfile) *Key {
82 return NewKey(pbkdf2.Key(pass, p.Salt, p.Iterations, 64, sha512.New)) 85 return NewKey(pbkdf2.Key(pass, p.Salt, p.Iterations, 64, sha512.New))
83} 86}
84 87
88type ODataOverview struct {
89 Title string `json:"title"`
90 URL string `json:"url"`
91}
92
93type OPDataSection struct {
94 Name string `json:"name"`
95 Title string `json:"title"`
96 Fields []OPDataField `json:"fields"`
97}
98
99type OPDataField struct {
100 ID string // id
101 Name string // name, n
102 Type string // type, t
103 Kind string // k
104 Value string // value, v
105 Designation string // designation
106}
107
85type OPBandItem struct { 108type OPBandItem struct {
86 Profile *OPProfile `json:"-"` 109 Profile *OPProfile `json:"-"`
87 UUID string `json:"uuid"` 110 UUID string `json:"uuid"`
@@ -398,49 +421,141 @@ func DeriveKey(data []byte, dkey *Key) (*Key, error) {
398 return NewKey(h.Sum(nil)), nil 421 return NewKey(h.Sum(nil)), nil
399} 422}
400 423
401func main() { 424type SearchResult struct {
402 datapath := "~/Dropbox/1Password/1Password.opvault" 425 Title string `json:"title"`
403 if strings.HasPrefix(datapath, "~/") { 426 URL string `json:"url"`
404 u, _ := user.Current() 427 BandItem *OPBandItem
405 datapath = u.HomeDir + datapath[1:len(datapath)] 428}
429
430func SearchOverviews(needle string, haystack map[string]*OPBandItem) (map[string]SearchResult, error) {
431 test, err := regexp.Compile(fmt.Sprintf("(?iU).*%s.*", needle))
432 if err != nil {
433 return nil, err
406 } 434 }
407 435
408 fmt.Print("Enter Password: ") 436 m := make(map[string]SearchResult)
409 password, err := terminal.ReadPassword(int(syscall.Stdin)) 437
438 // Simple linear search because the data set size is small
439 for k, v := range haystack {
440 if v.Trashed {
441 continue
442 }
443
444 var o SearchResult
445 if err = json.Unmarshal(v.Overview, &o); err != nil {
446 fmt.Printf("[ERROR] Decoding overview for %s\n", k)
447 continue
448 }
449
450 if test.MatchString(o.Title) || test.MatchString(o.URL) {
451 m[k] = SearchResult{o.Title, o.URL, v}
452 }
453 }
454
455 return m, nil
456}
457
458// TODO: a more sophisticated approach, perhaps
459func TruncateURL(u string) string {
460 if u == "" {
461 return u
462 }
463
464 p, err := url.Parse(u)
410 if err != nil { 465 if err != nil {
411 fmt.Println("Unable to read password") 466 if len(u) > 20 {
412 return 467 return fmt.Sprintf("%s...", u[:17])
468 } else {
469 return u
470 }
471 } else {
472 return fmt.Sprintf("%s://%s", p.Scheme, p.Host)
413 } 473 }
414 fmt.Println("") 474}
415 475
416 p, err := LoadProfile(datapath, password) 476// TODO: testing
477func doSearch(p *OPProfile) {
478 candidates, err := SearchOverviews(os.Args[1], p.Items)
417 if err != nil { 479 if err != nil {
418 fmt.Println(err) 480 fmt.Println("Error searching")
419 return 481 return
420 } 482 } else {
483 fmt.Printf("Found %d candidates\n", len(candidates))
484
485 outs := make([][]string, 0, len(candidates)+1)
486 c1len := 0
487
488 for k, v := range candidates {
489 c1l := len(v.BandItem.CategoryName)
490 if c1l > c1len {
491 c1len = c1l
492 }
493 outs = append(outs, []string{v.BandItem.CategoryName, k, v.Title, TruncateURL(v.URL)})
494 }
495
496 for _, i := range outs {
497 pad := ""
498 if len(i[0]) < (c1len + 5) {
499 pad = strings.Join(make([]string, c1len-(len(i[0])-1)), " ")
500 }
501 fmt.Printf("%s %s %s %s (%s)\n", i[0], pad, i[1], i[2], i[3])
502 }
421 503
422 // Login - CD05161569D347ADB401DE06D30A0A89 504 return
423 // Note - 7FF3565B434B47CF8906869BDCAD28C3 505 }
424 // Password - 7059A882C5F84DDCBD2EAD9EFFAA2B58 506}
425 // Router - 6009533D5A3B483A93FC7A843C39EDED
426 // Server - 7BDDE92045834A5386D56576DAEDDE54
427 // Credit - 6B9AFF656D264EEF8A887F79D243AE0D
428 // SW License - 7529DADA9453426BAF8B23A931834B2A
429 // Database - 102
430 // Email - 111
431 507
432 item, ok := p.Items[""] 508// TODO: testing
509func printItem(p *OPProfile, idx string) {
510 item, ok := p.Items[idx]
433 if !ok { 511 if !ok {
434 fmt.Println("UUID not found in profile") 512 fmt.Println("Error: No such item")
435 return 513 return
436 } 514 }
437 515
438 itemD, err := item.DecryptData() 516 itemDetails, err := item.DecryptData()
439 if err != nil { 517 if err != nil {
440 fmt.Println("Error decoding item data") 518 fmt.Println("Error decoding item data")
441 fmt.Println(err) 519 fmt.Println(err)
442 return 520 return
443 } 521 }
444 522
445 fmt.Printf("[\"%s\", %s, %s]", item.CategoryName, item.Overview, itemD) 523 fmt.Printf("[\"%s\", %s, %s]", item.CategoryName, item.Overview, itemDetails)
524}
525
526func ExpandUser(path string) (string, error) {
527 if strings.HasPrefix(datapath, "~/") {
528 if u, err := user.Current(); err != nil {
529 return nil, err
530 } else {
531 return fmt.Sprintf("%s%s", u.HomeDir, datapath[1:len(datapath)]), nil
532 }
533 }
534 return path, nil
535}
536
537func main() {
538 datapath, err := ExpandUser("~/Dropbox/1Password/1Password.opvault")
539 if err != nil {
540 fmt.Printf(err)
541 return
542 }
543
544 fmt.Print("Enter Password: ")
545 password, err := terminal.ReadPassword(int(syscall.Stdin))
546 if err != nil {
547 fmt.Println("Unable to read password")
548 return
549 }
550 fmt.Println("")
551
552 p, err := LoadProfile(datapath, password)
553 if err != nil {
554 fmt.Printf("ERROR: Unable to load keychain: %s", err)
555 return
556 }
557
558 // TODO: actually handle commands, maybe cobra?
559 doSearch(p)
560 //printItem(p, "")
446} 561}