-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
saltpack_pseudonym_resolvers.go
164 lines (143 loc) · 5.65 KB
/
saltpack_pseudonym_resolvers.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package saltpackkeys
import (
"fmt"
"strings"
"github.com/keybase/client/go/libkb"
"github.com/keybase/client/go/protocol/keybase1"
"github.com/keybase/client/go/teams"
"github.com/keybase/go-framed-msgpack-rpc/rpc"
"github.com/keybase/saltpack"
)
// KeyPseudonymResolver resolves new (team based) Key Pseudonyms, but falls back to old Kbfs Pseudonyms when it cannot find any match.
// A mock implementation (which does not communicate with the sever and avoids circular dependencies) is available in the saltpackkeysmocks package.
type KeyPseudonymResolver struct {
m libkb.MetaContext
}
var _ saltpack.SymmetricKeyResolver = (*KeyPseudonymResolver)(nil)
func NewKeyPseudonymResolver(m libkb.MetaContext) saltpack.SymmetricKeyResolver {
return &KeyPseudonymResolver{m: m}
}
func (r *KeyPseudonymResolver) ResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) {
keyPseudonyms := []libkb.KeyPseudonym{}
for _, identifier := range identifiers {
pseudonym := libkb.KeyPseudonym{}
if len(pseudonym) != len(identifier) {
return nil, fmt.Errorf("identifier is the wrong length for a key pseudonym (%d != %d)", len(pseudonym), len(identifier))
}
copy(pseudonym[:], identifier)
keyPseudonyms = append(keyPseudonyms, pseudonym)
}
results, err := libkb.GetKeyPseudonyms(r.m, keyPseudonyms)
if err != nil {
return nil, err
}
success := false
symmetricKeys := []*saltpack.SymmetricKey{}
for _, result := range results {
if result.Err != nil || result.Info.Application != keybase1.TeamApplication_SALTPACK {
r.m.Debug("skipping unresolved pseudonym: %s", result.Err)
symmetricKeys = append(symmetricKeys, nil)
continue
}
r.m.Debug("resolved pseudonym for %s, fetching key", result.Info.ID)
symmetricKey, err := r.getSymmetricKey(result.Info.ID, result.Info.KeyGen)
if err != nil {
return nil, err
}
success = true
symmetricKeys = append(symmetricKeys, symmetricKey)
}
if success {
return symmetricKeys, nil
}
r.m.Debug("No pseudonyms resolved, fallback to old kbfs pseudonyms")
// Fallback to old kbfs pseudonyms
return r.kbfsResolveKeys(identifiers)
}
func (r *KeyPseudonymResolver) getSymmetricKey(id keybase1.UserOrTeamID, gen libkb.KeyGen) (*saltpack.SymmetricKey, error) {
// For now resolving key pseudonyms for users is not necessary, as keybase encrypt does not
// use symmetric per user encryption keys.
team, err := teams.Load(r.m.Ctx(), r.m.G(), keybase1.LoadTeamArg{
ID: keybase1.TeamID(id),
})
if err != nil {
return nil, err
}
var key keybase1.TeamApplicationKey
key, err = team.SaltpackEncryptionKeyAtGeneration(r.m.Ctx(), keybase1.PerTeamKeyGeneration(gen))
if err != nil {
return nil, err
}
ssk := saltpack.SymmetricKey(key.Key)
return &ssk, nil
}
func (r *KeyPseudonymResolver) kbfsResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) {
tlfPseudonyms := []libkb.TlfPseudonym{}
for _, identifier := range identifiers {
pseudonym := libkb.TlfPseudonym{}
if len(pseudonym) != len(identifier) {
return nil, fmt.Errorf("identifier is the wrong length for a TLF pseudonym (%d != %d)", len(pseudonym), len(identifier))
}
copy(pseudonym[:], identifier)
tlfPseudonyms = append(tlfPseudonyms, pseudonym)
}
results, err := libkb.GetTlfPseudonyms(r.m.Ctx(), r.m.G(), tlfPseudonyms)
if err != nil {
return nil, err
}
symmetricKeys := []*saltpack.SymmetricKey{}
for _, result := range results {
if result.Err != nil {
r.m.Debug("skipping unresolved pseudonym: %s", result.Err)
symmetricKeys = append(symmetricKeys, nil)
continue
}
r.m.Debug("resolved pseudonym for %s, fetching key", result.Info.Name)
symmetricKey, err := r.kbfsGetSymmetricKey(r.m, *result.Info)
if err != nil {
return nil, err
}
symmetricKeys = append(symmetricKeys, symmetricKey)
}
return symmetricKeys, nil
}
func (r *KeyPseudonymResolver) getCryptKeys(m libkb.MetaContext, name string) (keybase1.GetTLFCryptKeysRes, error) {
xp := m.G().ConnectionManager.LookupByClientType(keybase1.ClientType_KBFS)
if xp == nil {
return keybase1.GetTLFCryptKeysRes{}, libkb.KBFSNotRunningError{}
}
cli := &keybase1.TlfKeysClient{
Cli: rpc.NewClient(xp, libkb.NewContextifiedErrorUnwrapper(r.m.G()), libkb.LogTagsFromContext),
}
return cli.GetTLFCryptKeys(m.Ctx(), keybase1.TLFQuery{
TlfName: name,
IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_CLI,
})
}
func (r *KeyPseudonymResolver) kbfsGetSymmetricKey(m libkb.MetaContext, info libkb.TlfPseudonymServerInfo) (*saltpack.SymmetricKey, error) {
// NOTE: In order to handle finalized TLFs (which is one of the main
// benefits of using TLF keys to begin with, for forward readability), we
// need the server to tell us what the current, potentially-finalized name
// of the TLF is. If that's not the same as what the name was when the
// message was sent, we can't necessarily check that the server is being
// honest. That's ok insofar as we're not relying on these keys for
// authenticity, but it's a drag to not be able to use the pseudonym
// machinery.
// TODO: Check as much as we can, if the original TLF was fully resolved.
// Strip "/keybase/private/" from the name.
basename := strings.TrimPrefix(info.UntrustedCurrentName, "/keybase/private/")
if len(basename) >= len(info.UntrustedCurrentName) {
return nil, fmt.Errorf("unexpected prefix, expected '/keybase/private', found %q", info.UntrustedCurrentName)
}
res, err := r.getCryptKeys(m, basename)
if err != nil {
return nil, err
}
for _, key := range res.CryptKeys {
if libkb.KeyGen(key.KeyGeneration) == info.KeyGen {
// Success!
return (*saltpack.SymmetricKey)(&key.Key), nil
}
}
return nil, fmt.Errorf("no keys in TLF %q matched generation %d", basename, info.KeyGen)
}