1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package jsonsign
18
19 import (
20 "errors"
21 "fmt"
22 "log"
23 "os"
24
25 "golang.org/x/crypto/openpgp"
26 "perkeep.org/internal/gpgagent"
27 "perkeep.org/internal/pinentry"
28 )
29
30 func (fe *FileEntityFetcher) decryptEntity(e *openpgp.Entity) error {
31
32 pubk := &e.PrivateKey.PublicKey
33 desc := fmt.Sprintf("Need to unlock GPG key %s to use it for signing.",
34 pubk.KeyIdShortString())
35
36 conn, err := gpgagent.NewConn()
37 switch err {
38 case gpgagent.ErrNoAgent:
39 fmt.Fprintf(os.Stderr, "Note: gpg-agent not found; resorting to on-demand password entry.\n")
40 case nil:
41 defer conn.Close()
42 req := &gpgagent.PassphraseRequest{
43 CacheKey: "camli:jsonsign:" + pubk.KeyIdShortString(),
44 Prompt: "Passphrase",
45 Desc: desc,
46 }
47 for tries := 0; tries < 2; tries++ {
48 pass, err := conn.GetPassphrase(req)
49 if err == nil {
50 err = e.PrivateKey.Decrypt([]byte(pass))
51 if err == nil {
52 return nil
53 }
54 req.Error = "Passphrase failed to decrypt: " + err.Error()
55 conn.RemoveFromCache(req.CacheKey)
56 continue
57 }
58 if err == gpgagent.ErrCancel {
59 return errors.New("jsonsign: failed to decrypt key; action canceled")
60 }
61 log.Printf("jsonsign: gpgagent: %v", err)
62 }
63 default:
64 log.Printf("jsonsign: gpgagent: %v", err)
65 }
66
67 pinReq := &pinentry.Request{Desc: desc, Prompt: "Passphrase"}
68 for tries := 0; tries < 2; tries++ {
69 pass, err := pinReq.GetPIN()
70 if err == nil {
71 err = e.PrivateKey.Decrypt([]byte(pass))
72 if err == nil {
73 return nil
74 }
75 pinReq.Error = "Passphrase failed to decrypt: " + err.Error()
76 continue
77 }
78 if err == pinentry.ErrCancel {
79 return errors.New("jsonsign: failed to decrypt key; action canceled")
80 }
81 log.Printf("jsonsign: pinentry: %v", err)
82 }
83 return fmt.Errorf("jsonsign: failed to decrypt key %q", pubk.KeyIdShortString())
84 }