// decryptEntity calls gnupg-agent and pinentry to obtain a passphrase and // decrypt the private key of a given entity (thank you, camlistore folks) func decryptEntity(s *openpgp.Entity) (ds *openpgp.Entity, err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("decryptEntity(): %v", e) } }() ds = s // TODO: syscall.Mlock a region and keep pass phrase in it. pubk := &ds.PrivateKey.PublicKey desc := fmt.Sprintf("Need to unlock GPG key %s to use it for signing.", pubk.KeyIdShortString()) conn, err := gpgagent.NewConn() switch err { case gpgagent.ErrNoAgent: fmt.Fprintf(os.Stderr, "Note: gpg-agent not found; resorting to on-demand password entry.\n") case nil: defer conn.Close() req := &gpgagent.PassphraseRequest{ CacheKey: "go:pgpencrypt:" + pubk.KeyIdShortString(), Prompt: "Passphrase", Desc: desc, } for tries := 0; tries < 3; tries++ { pass, err := conn.GetPassphrase(req) if err == nil { err = ds.PrivateKey.Decrypt([]byte(pass)) if err == nil { return ds, err } req.Error = "Passphrase failed to decrypt: " + err.Error() conn.RemoveFromCache(req.CacheKey) continue } if err == gpgagent.ErrCancel { panic("failed to decrypt key; action canceled") } } default: panic(err) } pinReq := &pinentry.Request{Desc: desc, Prompt: "Passphrase"} for tries := 0; tries < 3; tries++ { pass, err := pinReq.GetPIN() if err == nil { err = ds.PrivateKey.Decrypt([]byte(pass)) if err == nil { return ds, err } pinReq.Error = "Passphrase failed to decrypt: " + err.Error() continue } if err == pinentry.ErrCancel { panic("failed to decrypt key; action canceled") } } return ds, fmt.Errorf("decryptEntity(): failed to decrypt key %q: %v", pubk.KeyIdShortString(), err) }
func (fe *FileEntityFetcher) decryptEntity(e *openpgp.Entity) error { // TODO: syscall.Mlock a region and keep pass phrase in it. pubk := &e.PrivateKey.PublicKey desc := fmt.Sprintf("Need to unlock GPG key %s to use it for signing.", pubk.KeyIdShortString()) conn, err := gpgagent.NewConn() switch err { case gpgagent.ErrNoAgent: fmt.Fprintf(os.Stderr, "Note: gpg-agent not found; resorting to on-demand password entry.\n") case nil: defer conn.Close() req := &gpgagent.PassphraseRequest{ CacheKey: "camli:jsonsign:" + pubk.KeyIdShortString(), Prompt: "Passphrase", Desc: desc, } for tries := 0; tries < 2; tries++ { pass, err := conn.GetPassphrase(req) if err == nil { err = e.PrivateKey.Decrypt([]byte(pass)) if err == nil { return nil } req.Error = "Passphrase failed to decrypt: " + err.Error() conn.RemoveFromCache(req.CacheKey) continue } if err == gpgagent.ErrCancel { return errors.New("jsonsign: failed to decrypt key; action canceled") } log.Printf("jsonsign: gpgagent: %v", err) } default: log.Printf("jsonsign: gpgagent: %v", err) } pinReq := &pinentry.Request{Desc: desc, Prompt: "Passphrase"} for tries := 0; tries < 2; tries++ { pass, err := pinReq.GetPIN() if err == nil { err = e.PrivateKey.Decrypt([]byte(pass)) if err == nil { return nil } pinReq.Error = "Passphrase failed to decrypt: " + err.Error() continue } if err == pinentry.ErrCancel { return errors.New("jsonsign: failed to decrypt key; action canceled") } log.Printf("jsonsign: pinentry: %v", err) } return fmt.Errorf("jsonsign: failed to decrypt key %q", pubk.KeyIdShortString()) }