forked from gopasspw/gopass
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
125 lines (112 loc) · 2.69 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package openpgp
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"golang.org/x/crypto/openpgp"
"github.com/justwatchcom/gopass/pkg/out"
homedir "github.com/mitchellh/go-homedir"
)
type agentClient interface {
Ping() error
Passphrase(string, string) (string, error)
Remove(string) error
}
var maxUnlockAttempts = 3
func (g *GPG) mkPromptFunc() func([]openpgp.Key, bool) ([]byte, error) {
attempt := 0
return func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
attempt++
if attempt > maxUnlockAttempts {
return nil, fmt.Errorf("out of retries")
}
for i, key := range keys {
if key.PublicKey == nil || key.PrivateKey == nil {
continue
}
fp := key.PublicKey.KeyIdString()
passphrase, err := g.client.Passphrase(fp, fmt.Sprintf("Unlock private key %s", fp))
if err != nil {
continue
}
if err := keys[i].PrivateKey.Decrypt([]byte(passphrase)); err == nil {
return []byte(passphrase), nil
}
if err := g.client.Remove(fp); err != nil {
return nil, err
}
time.Sleep(10 * time.Millisecond)
}
return nil, nil
}
}
func (g *GPG) findEntity(id string) *openpgp.Entity {
return g.findEntityInLists(id, g.secring, g.pubring)
}
func (g *GPG) findEntityInLists(id string, els ...openpgp.EntityList) *openpgp.Entity {
id = strings.TrimPrefix(id, "0x")
for _, el := range els {
for _, ent := range el {
if ent.PrimaryKey == nil {
continue
}
fp := fmt.Sprintf("%X", ent.PrimaryKey.Fingerprint)
if strings.HasSuffix(fp, id) {
return ent
}
}
}
return nil
}
func (g *GPG) recipientsToEntities(recipients []string) []*openpgp.Entity {
ents := make([]*openpgp.Entity, 0, len(recipients))
for _, key := range g.pubring {
if key.PrimaryKey == nil {
continue
}
fp := fmt.Sprintf("%X", key.PrimaryKey.Fingerprint)
for _, recp := range recipients {
recp = strings.TrimPrefix(recp, "0x")
if strings.HasSuffix(fp, recp) {
ents = append(ents, key)
}
}
}
return ents
}
func listKeyIDs(el openpgp.EntityList) []string {
ids := make([]string, 0, len(el))
for _, key := range el {
if key.PrimaryKey == nil {
continue
}
ids = append(ids, key.PrimaryKey.KeyIdString())
}
return ids
}
func readKeyring(fn string) (openpgp.EntityList, error) {
fh, err := os.Open(fn)
if err != nil {
if os.IsNotExist(err) {
return openpgp.EntityList{}, nil
}
return nil, err
}
defer fh.Close()
return openpgp.ReadKeyRing(fh)
}
// gpgHome returns the gnupg homedir
func gpgHome(ctx context.Context) string {
if gh := os.Getenv("GNUPGHOME"); gh != "" {
return gh
}
hd, err := homedir.Dir()
if err != nil {
out.Debug(ctx, "Failed to get homedir: %s", err)
return ""
}
return filepath.Join(hd, ".gnupg")
}