func decrypt(ringPath string, input io.Reader) (io.Reader, error) { ringFile, err := os.Open(ringPath) if err != nil { return nil, err } defer ringFile.Close() ring, err := openpgp.ReadKeyRing(ringFile) if err != nil { return nil, err } var keyToTry, attempt int var triedCache bool promptFunc := openpgp.PromptFunction(func(keys []openpgp.Key, symmetric bool) ([]byte, error) { if keyToTry >= len(keys) { return nil, fmt.Errorf("no more keys to try") } if attempt > 2 { attempt = 0 keyToTry++ return nil, nil } defer func() { attempt++ }() key := keys[keyToTry] fingerprint := fmt.Sprintf("%X", key.PublicKey.Fingerprint) if !triedCache { triedCache = true if cachedPass, _ := passphrase.GetPassphrase(fingerprint, "", "", "", false, false); cachedPass != "" { if err := key.PrivateKey.Decrypt([]byte(cachedPass)); err == nil { return nil, nil } } } passphrase.ClearCachedPassphrase(fingerprint) prompt := "" description := fmt.Sprintf("Key %s; attempt %d", key.PublicKey.KeyIdShortString(), attempt+1) passwd, err := passphrase.GetPassphrase(fingerprint, prompt, description, "", true, false) if err != nil { return nil, err } key.PrivateKey.Decrypt([]byte(passwd)) return nil, nil }) msgDetails, err := openpgp.ReadMessage(input, ring, promptFunc, nil) if err != nil { return nil, err } return msgDetails.UnverifiedBody, nil }
func (cmd *add) Run(globals options.Options, args []string) error { prefix := string(globals.Prefix) name, passfile, err := getNameAndFile(prefix, args) if err != nil { return err } if _, err := os.Stat(passfile); err == nil && !cmd.force { return &CmdError{4, "an entry already exists for " + name + ". Use -force to overwrite it"} } passdir := filepath.Dir(passfile) if _, err := os.Stat(passdir); err != nil && os.IsNotExist(err) { if err := os.MkdirAll(passdir, 0777); err != nil { return ErrNotAdding(name, "could not create directory "+passdir) } } outfile, err := os.Create(passfile) if err != nil { return ErrNotAdding(name, "could not open file") } defer outfile.Close() var input io.Reader = os.Stdin if cmd.edit != "" { tempfile, err := ioutil.TempFile("", "pass_"+name) defer os.Remove(tempfile.Name()) if err != nil { return ErrNotAdding(name, "could not create temporary file for editing") } editor := exec.Command(cmd.edit, tempfile.Name()) editor.Stdin = os.Stdin editor.Stdout = os.Stdout editor.Stderr = os.Stderr if err := editor.Run(); err != nil { return ErrNotAdding(name, "failed running editor: "+cmd.edit) } input = tempfile } else if isTerminal(os.Stdin) { passwd, err := passphrase.GetPassphrase("", "", "Please enter a password for "+name, "", true, true) if err != nil { return ErrNotAdding(name, "could not get a passphrase") } input = strings.NewReader(passwd + "\n") } if err := encrypt(string(globals.PubRing), input, outfile); err != nil { return ErrNotAdding(name, "could not encrypt passphrase") } relative := passfile[len(prefix)+1:] if err := addAndCommit(prefix, relative, "Add password for "+name); err != nil { return &CmdError{4, "failed to commit passphrase to repository"} } return nil }