//+build darwin package passgo import ( "bufio" "bytes" "encoding/hex" "fmt" "os" "os/exec" "os/user" "path" "strings" "strconv" "sync" "golang.org/x/crypto/openpgp" "github.com/jcmdev0/gpgagent" "github.com/fsnotify/fsnotify" ) func (s *Store) nativeDecrypt(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) nativeEncrypt(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 nativeIdentities() ([]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() { // get UID of current user usr, err := user.Current() if err != nil { fmt.Printf("Error: cannot get user ID: %s\n", err) return } uid := usr.Uid // get GPG Agent PID // look for gpg-agent running as the current user: output, err := exec.Command("pgrep", "-U", uid, "gpg-agent").Output() if err != nil { fmt.Printf("Error: %s\n", err) return } else { fmt.Printf("gpg-agent process number is %s\n", output) } // trim trailing /n and convert to int pid, err := strconv.Atoi(string(output[:len(output)-1])) if err != nil { fmt.Printf("Integer conversion failed: %s\n", err) return } // find agent socket file // find unix domain sockets opened by the current user's gpg-agent cmd := exec.Command("lsof", "-w", "-Fn", "-u", uid, "-baUcgpg-agent") stdout, err := cmd.StdoutPipe() if err != nil { fmt.Printf("Error: connect stdout to lsof: %s\n", err) return } scanner := bufio.NewScanner(stdout) err = cmd.Start() if err != nil { fmt.Printf("Error: cannot run lsof: %s\n", err) } // look for a socket named "S.gpg-agent" filename := "" for scanner.Scan() { x := scanner.Text() if x[0] != 'n' { continue } x = x[1:] if path.Base(x) == "S.gpg-agent" { fmt.Println(x) filename = x } } cmd.Wait() if filename == "" { fmt.Printf("Error: gpg-agent socket file not found\n") return } s := fmt.Sprintf("%s:%d:1", filename, pid) fmt.Printf("GPG_AGENT_INFO = %s\n", s) os.Setenv("GPG_AGENT_INFO", s) // gpg-agent is running, so use GPGPrompt as password prompt ask = GPGPrompt } func GPGPrompt(keys []openpgp.Key, symmetric bool) ([]byte, error) { conn, err := gpgagent.NewGpgAgentConn() if err != nil { return nil, err } defer conn.Close() for _, key := range keys { cacheId := strings.ToUpper(hex.EncodeToString(key.PublicKey.Fingerprint[:])) request := gpgagent.PassphraseRequest{CacheKey: cacheId} for i := 0; i < 3; i++ { var passphrase string passphrase, err = conn.GetPassphrase(&request) if err != nil { continue } err = key.PrivateKey.Decrypt([]byte(passphrase)) if err != nil { conn.RemoveFromCache(cacheId) continue } return []byte(passphrase), nil } return nil, err } return nil, fmt.Errorf("Unable to find key") } func init() { 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 func Clip(x string) { b := bytes.NewBuffer([]byte(x)) cmd := exec.Command("pbcopy") cmd.Stdin = b cmd.Run() }