/
upak_lite.go
363 lines (328 loc) · 10.4 KB
/
upak_lite.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
package libkb
import (
"fmt"
"io"
"github.com/buger/jsonparser"
"github.com/adamwalz/keybase-client/go/jsonparserw"
keybase1 "github.com/adamwalz/keybase-client/go/protocol/keybase1"
)
func LoadUPAKLite(arg LoadUserArg) (ret *keybase1.UPKLiteV1AllIncarnations, err error) {
uid := arg.uid
m := arg.m
user, err := LoadUserFromServer(m, uid, nil)
if err != nil {
return nil, err
}
leaf, err := lookupMerkleLeaf(m, uid, false, nil, MerkleOpts{NoServerPolling: false})
if err != nil {
return nil, err
}
loader := NewHighSigChainLoader(m, user, leaf)
highChain, err := loader.Load()
if err != nil {
return nil, err
}
return buildUPKLiteAllIncarnations(m, user, leaf, highChain)
}
type HighSigChainLoader struct {
MetaContextified
user *User
leaf *MerkleUserLeaf
chain *HighSigChain
chainType *ChainType
ckf ComputedKeyFamily
dirtyTail *MerkleTriple
}
type HighSigChain struct {
MetaContextified
uid keybase1.UID
username NormalizedUsername
chainLinks ChainLinks
// for a locally delegated key
localCki *ComputedKeyInfos
// For a user who has never done a reset, this is 1.
currentSubchainStart keybase1.Seqno
prevSubchains []ChainLinks
}
const UPKLiteMinorVersionCurrent = keybase1.UPKLiteMinorVersion_V0
func NewHighSigChainLoader(m MetaContext, user *User, leaf *MerkleUserLeaf) *HighSigChainLoader {
hsc := HighSigChain{
MetaContextified: NewMetaContextified(m),
uid: user.GetUID(),
username: user.GetNormalizedName(),
}
loader := HighSigChainLoader{
user: user,
leaf: leaf,
chain: &hsc,
chainType: PublicChain,
MetaContextified: NewMetaContextified(m),
}
loader.ckf.kf = user.GetKeyFamily()
return &loader
}
func (l *HighSigChainLoader) Load() (ret *HighSigChain, err error) {
// request new links (and the unverified tail) from the server
// and put them into the highSigChain
err = l.LoadFromServer()
if err != nil {
return nil, err
}
// verify the chain
err = l.chain.VerifyChain(l.M())
if err != nil {
return nil, err
}
// compute keys
err = l.VerifySigsAndComputeKeys()
if err != nil {
return nil, err
}
return l.chain, nil
}
func (l *HighSigChainLoader) selfUID() (uid keybase1.UID) {
// for now let's always assume this isn't applicable, because there's
// no distinction for loading yourself
return uid
}
func (l *HighSigChainLoader) LoadFromServer() (err error) {
srv := l.GetMerkleTriple()
l.dirtyTail, err = l.chain.LoadFromServer(l.M(), srv, l.selfUID())
return err
}
func (hsc *HighSigChain) LoadFromServer(m MetaContext, t *MerkleTriple, selfUID keybase1.UID) (dirtyTail *MerkleTriple, err error) {
// get the high sigs from the server
// ------------------
m, tbs := m.WithTimeBuckets()
apiArg := APIArg{
Endpoint: "sig/get_high",
SessionType: APISessionTypeOPTIONAL,
Args: HTTPArgs{
"uid": S{Val: hsc.uid.String()},
"c3": I{Val: int(sigCompression3Unstubbed)},
},
}
resp, finisher, err := m.G().API.GetResp(m, apiArg)
if err != nil {
return nil, err
}
defer finisher()
recordFin := tbs.Record("HighSigChain.LoadFromServer.ReadAll")
body, err := io.ReadAll(resp.Body)
if err != nil {
recordFin()
return nil, err
}
recordFin()
// parse the response
// ------------------
if val, err := jsonparserw.GetInt(body, "status", "code"); err == nil {
if keybase1.StatusCode(val) == keybase1.StatusCode_SCDeleted {
return nil, UserDeletedError{}
}
}
var links ChainLinks
var lastLink *ChainLink
_, err = jsonparserw.ArrayEach(body, func(value []byte, dataType jsonparser.ValueType, offset int, inErr error) {
var link *ChainLink
parentSigChain := &SigChain{} // because we don't want the cache to use these
link, err = ImportLinkFromServer(m, parentSigChain, value, selfUID)
if err != nil {
m.Debug("Error importing link: %s", err.Error())
}
links = append(links, link)
lastLink = link
}, "sigs")
if err != nil {
return nil, err
}
foundTail, err := lastLink.checkAgainstMerkleTree(t)
if err != nil {
return nil, err
}
if !foundTail {
err = fmt.Errorf("Last link is not the tail")
return nil, err
}
dirtyTail = lastLink.ToMerkleTriple()
hsc.chainLinks = links
return dirtyTail, err
}
func (hsc *HighSigChain) VerifyChain(m MetaContext) (err error) {
defer m.Trace("HighSigChain.VerifyChain", &err)()
for i := len(hsc.chainLinks) - 1; i >= 0; i-- {
curr := hsc.chainLinks[i]
m.VLogf(VLog1, "| verify high chain link %d (%s)", curr.GetSeqno(), curr.id)
if err = curr.VerifyLink(); err != nil {
return err
}
var expectedPrevID LinkID
var expectedPrevSeqno keybase1.Seqno
if curr.GetHighSkip() != nil {
expectedPrevSeqno = curr.GetHighSkip().Seqno
expectedPrevID = curr.GetHighSkip().Hash
} else {
// fallback to normal prevs if the link doesn't have a high_skip
expectedPrevSeqno = curr.GetSeqno() - 1
expectedPrevID = curr.GetPrev()
}
if i == 0 && (expectedPrevSeqno != 0 || expectedPrevID != nil) {
return ChainLinkPrevHashMismatchError{
fmt.Sprintf("The first link should have 0,nil for it's expected previous. It had %d, %s", expectedPrevSeqno, expectedPrevID),
}
}
if i > 0 {
prev := hsc.chainLinks[i-1]
if !prev.id.Eq(expectedPrevID) {
return ChainLinkPrevHashMismatchError{fmt.Sprintf("Chain mismatch at seqno=%d", curr.GetSeqno())}
}
if prev.GetSeqno() != expectedPrevSeqno {
return ChainLinkWrongSeqnoError{fmt.Sprintf("Chain seqno mismatch at seqno=%d (previous=%d)", curr.GetSeqno(), prev.GetSeqno())}
}
}
if err = curr.CheckNameAndID(hsc.username, hsc.uid); err != nil {
return err
}
// this isn't being used for anything right now, but it might be useful
// if we ever want to do caching, especially as it can be distinguished
// from the other field, chainVerified
curr.highChainVerified = true
}
return nil
}
func (l *HighSigChainLoader) VerifySigsAndComputeKeys() (err error) {
_, err = l.chain.VerifySigsAndComputeKeys(l.M(), l.leaf.eldest, &l.ckf)
return err
}
func (hsc *HighSigChain) verifySigsAndComputeKeysCurrent(m MetaContext, eldest keybase1.KID, ckf *ComputedKeyFamily) (linksConsumed int, err error) {
// this is the case immediately after a reset
if ckf.kf == nil || eldest.IsNil() {
m.VLogf(VLog1, "UPAKLite short-circuit verifySigsAndComputeKeysCurrent, since no Key available")
hsc.localCki = NewComputedKeyInfos(hsc.G())
ckf.cki = hsc.localCki
return 0, err
}
var links ChainLinks
links, err = cropToRightmostSubchain(m, hsc.chainLinks, eldest, hsc.uid)
if err != nil {
return 0, err
}
if len(links) == 0 {
// actually, not sure how this can happen without eldest being nil
m.VLogf(VLog1, "Empty chain after we limited to eldest %s", eldest)
hsc.localCki = NewComputedKeyInfos(hsc.G())
ckf.cki = hsc.localCki
return 0, nil
}
hsc.currentSubchainStart = links[0].GetSeqno()
m.VLogf(VLog1, "UPAKLite verifying current chain starting at %d", int(hsc.currentSubchainStart))
_, ckf.cki, err = verifySubchain(m, hsc.username, *ckf.kf, links)
return len(links), err
}
func (hsc *HighSigChain) VerifySigsAndComputeKeys(m MetaContext, eldest keybase1.KID, ckf *ComputedKeyFamily) (cached bool, err error) {
username := hsc.username
uid := hsc.uid
hsc.currentSubchainStart = 0
// current
linksConsumed, err := hsc.verifySigsAndComputeKeysCurrent(m, eldest, ckf)
if err != nil || ckf.kf == nil {
return false, err
}
// historical
historicalLinks := hsc.chainLinks.omittingNRightmostLinks(linksConsumed)
if len(historicalLinks) > 0 {
m.VLogf(VLog1, "After consuming %d links, there are %d historical links left",
linksConsumed, len(historicalLinks))
// errors are ignored from this method in full sigchain loads also, because we'd rather
// not block current behavior from a failure in here.
_, prevSubchains, _ := verifySigsAndComputeKeysHistorical(m, uid, username, historicalLinks, *ckf.kf)
hsc.prevSubchains = prevSubchains
}
return false, nil
}
func (l *HighSigChainLoader) GetMerkleTriple() (ret *MerkleTriple) {
// leaf is what the server said was the leaf for the user
if l.leaf != nil {
ret = l.chainType.GetMerkleTriple(l.leaf)
}
return ret
}
func extractDeviceKeys(cki *ComputedKeyInfos) *map[keybase1.KID]keybase1.PublicKeyV2NaCl {
deviceKeys := make(map[keybase1.KID]keybase1.PublicKeyV2NaCl)
if cki != nil {
for kid := range cki.Infos {
if !KIDIsPGP(kid) {
deviceKeys[kid] = cki.exportDeviceKeyV2(kid)
}
}
}
return &deviceKeys
}
func buildUPKLiteAllIncarnations(m MetaContext, user *User, leaf *MerkleUserLeaf, hsc *HighSigChain) (ret *keybase1.UPKLiteV1AllIncarnations, err error) {
// build the current UPKLiteV1
uid := user.GetUID()
name := user.GetName()
status := user.GetStatus()
eldestSeqno := hsc.currentSubchainStart
deviceKeys := extractDeviceKeys(hsc.GetComputedKeyInfos())
currentUpk := keybase1.UPKLiteV1{
Uid: uid,
Username: name,
EldestSeqno: eldestSeqno,
Status: status,
DeviceKeys: *deviceKeys,
Reset: nil,
}
// build the historical (aka PastIncarnations) UPKLiteV1s
var previousUpks []keybase1.UPKLiteV1
resetMap := make(map[int](*keybase1.ResetSummary))
if resets := leaf.resets; resets != nil {
for _, l := range resets.chain {
tmp := l.Summarize()
resetMap[int(l.ResetSeqno)] = &tmp
}
}
for idx, subchain := range hsc.prevSubchains {
latestLink := subchain[len(subchain)-1]
eldestSeqno = subchain[0].GetSeqno()
reset := resetMap[idx+1]
if reset != nil {
reset.EldestSeqno = eldestSeqno
}
prevDeviceKeys := extractDeviceKeys(latestLink.cki)
prevUpk := keybase1.UPKLiteV1{
Uid: uid,
Username: name,
EldestSeqno: eldestSeqno,
Status: status,
DeviceKeys: *prevDeviceKeys,
Reset: reset,
}
previousUpks = append(previousUpks, prevUpk)
}
// Collect the link IDs (that is, the hashes of the signature inputs) from all chainlinks.
linkIDs := map[keybase1.Seqno]keybase1.LinkID{}
for _, link := range hsc.chainLinks {
linkIDs[link.GetSeqno()] = link.LinkID().Export()
}
final := keybase1.UPKLiteV1AllIncarnations{
Current: currentUpk,
PastIncarnations: previousUpks,
SeqnoLinkIDs: linkIDs,
MinorVersion: UPKLiteMinorVersionCurrent,
}
ret = &final
return ret, err
}
func (hsc HighSigChain) GetComputedKeyInfos() (cki *ComputedKeyInfos) {
cki = hsc.localCki
if cki == nil {
if l := last(hsc.chainLinks); l != nil {
if l.cki == nil {
hsc.G().Log.Debug("GetComputedKeyInfos: l.cki is nil")
}
cki = l.cki
}
}
return cki
}