/
key_bundle_v3.go
543 lines (461 loc) · 16.7 KB
/
key_bundle_v3.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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
package kbfsmd
import (
"encoding"
"fmt"
"reflect"
"github.com/adamwalz/keybase-client/go/kbfs/cache"
"github.com/adamwalz/keybase-client/go/kbfs/kbfscodec"
"github.com/adamwalz/keybase-client/go/kbfs/kbfscrypto"
"github.com/adamwalz/keybase-client/go/kbfs/kbfshash"
"github.com/adamwalz/keybase-client/go/protocol/keybase1"
"github.com/keybase/go-codec/codec"
"github.com/pkg/errors"
)
// A lot of this code is duplicated from key_bundle_v3.go, except with
// DeviceKeyInfoMapV2 (keyed by keybase1.KID) replaced with
// DeviceKeyInfoMapV3 (keyed by kbfscrypto.CryptPublicKey).
// DeviceKeyInfoMapV3 is a map from a user devices (identified by the
// corresponding device CryptPublicKey) to the TLF's symmetric secret
// key information.
type DeviceKeyInfoMapV3 map[kbfscrypto.CryptPublicKey]TLFCryptKeyInfo
// static sizes in DeviceKeyInfoMapV3
var (
ssCryptPublicKey = int(reflect.TypeOf(kbfscrypto.CryptPublicKey{}).Size())
ssTLFCryptKeyInfo = int(reflect.TypeOf(TLFCryptKeyInfo{}).Size())
)
// Size implements the cache.Measurable interface.
func (dkimV3 DeviceKeyInfoMapV3) Size() int {
// statically-sized part
mapSize := cache.StaticSizeOfMapWithSize(
ssCryptPublicKey, ssTLFCryptKeyInfo, len(dkimV3))
// go through pointer type content
var contentSize int
for k, v := range dkimV3 {
contentSize += len(k.KID())
contentSize += len(v.ServerHalfID.ID.String())
// We are not using v.ClientHalf.encryptedData here since that would
// include the size of struct itself which is already counted in
// cache.StaticSizeOfMapWithSize.
contentSize += len(v.ClientHalf.EncryptedData) +
len(v.ClientHalf.Nonce)
}
return mapSize + contentSize
}
var _ cache.Measurable = DeviceKeyInfoMapV3{}
func (dkimV3 DeviceKeyInfoMapV3) fillInDeviceInfos(
uid keybase1.UID, tlfCryptKey kbfscrypto.TLFCryptKey,
ePrivKey kbfscrypto.TLFEphemeralPrivateKey, ePubIndex int,
updatedDeviceKeys DevicePublicKeys) (
serverHalves DeviceKeyServerHalves, err error) {
serverHalves = make(DeviceKeyServerHalves, len(updatedDeviceKeys))
// TODO: parallelize
for k := range updatedDeviceKeys {
// Skip existing entries, and only fill in new ones
if _, ok := dkimV3[k]; ok {
continue
}
clientInfo, serverHalf, err := splitTLFCryptKey(
uid, tlfCryptKey, ePrivKey, ePubIndex, k)
if err != nil {
return nil, err
}
dkimV3[k] = clientInfo
serverHalves[k] = serverHalf
}
return serverHalves, nil
}
func (dkimV3 DeviceKeyInfoMapV3) toPublicKeys() DevicePublicKeys {
publicKeys := make(DevicePublicKeys, len(dkimV3))
for key := range dkimV3 {
publicKeys[key] = true
}
return publicKeys
}
// UserDeviceKeyInfoMapV3 maps a user's keybase UID to their
// DeviceKeyInfoMapV3.
type UserDeviceKeyInfoMapV3 map[keybase1.UID]DeviceKeyInfoMapV3
// Size implements the cache.Measurable interface.
func (udkimV3 UserDeviceKeyInfoMapV3) Size() int {
// statically-sized part
mapSize := cache.StaticSizeOfMapWithSize(
cache.PtrSize, cache.PtrSize, len(udkimV3))
// go through pointer type content
var contentSize int
for k, v := range udkimV3 {
contentSize += len(k) + v.Size()
}
return mapSize + contentSize
}
var _ cache.Measurable = UserDeviceKeyInfoMapV3{}
// ToPublicKeys converts this object to a UserDevicePublicKeys object.
func (udkimV3 UserDeviceKeyInfoMapV3) ToPublicKeys() UserDevicePublicKeys {
publicKeys := make(UserDevicePublicKeys, len(udkimV3))
for u, dkimV3 := range udkimV3 {
publicKeys[u] = dkimV3.toPublicKeys()
}
return publicKeys
}
func writerUDKIMV2ToV3(codec kbfscodec.Codec, udkimV2 UserDeviceKeyInfoMapV2,
ePubKeyCount int) (
UserDeviceKeyInfoMapV3, error) {
udkimV3 := make(UserDeviceKeyInfoMapV3, len(udkimV2))
for uid, dkimV2 := range udkimV2 {
dkimV3 := make(DeviceKeyInfoMapV3, len(dkimV2))
for kid, info := range dkimV2 {
index := info.EPubKeyIndex
if index < 0 {
// TODO: Fix this; see KBFS-1719.
return nil, fmt.Errorf(
"Writer key with index %d for user=%s, kid=%s not handled yet",
index, uid, kid)
}
if index >= ePubKeyCount {
return nil, fmt.Errorf(
"Invalid writer key index %d for user=%s, kid=%s",
index, uid, kid)
}
var infoCopy TLFCryptKeyInfo
err := kbfscodec.Update(codec, &infoCopy, info)
if err != nil {
return nil, err
}
dkimV3[kbfscrypto.MakeCryptPublicKey(kid)] = infoCopy
}
udkimV3[uid] = dkimV3
}
return udkimV3, nil
}
// RemoveDevicesNotIn removes any info for any device that is not
// contained in the given map of users and devices.
func (udkimV3 UserDeviceKeyInfoMapV3) RemoveDevicesNotIn(
updatedUserKeys UserDevicePublicKeys) ServerHalfRemovalInfo {
removalInfo := make(ServerHalfRemovalInfo)
for uid, dkim := range udkimV3 {
userRemoved := false
deviceServerHalfIDs := make(DeviceServerHalfRemovalInfo)
if deviceKeys, ok := updatedUserKeys[uid]; ok {
for key, info := range dkim {
if !deviceKeys[key] {
delete(dkim, key)
deviceServerHalfIDs[key] = append(
deviceServerHalfIDs[key],
info.ServerHalfID)
}
}
if len(deviceServerHalfIDs) == 0 {
continue
}
} else {
// The user was completely removed, which
// shouldn't happen but might as well make it
// work just in case.
userRemoved = true
for key, info := range dkim {
deviceServerHalfIDs[key] = append(
deviceServerHalfIDs[key],
info.ServerHalfID)
}
delete(udkimV3, uid)
}
removalInfo[uid] = UserServerHalfRemovalInfo{
UserRemoved: userRemoved,
DeviceServerHalfIDs: deviceServerHalfIDs,
}
}
return removalInfo
}
// FillInUserInfos fills in this map from the given info.
func (udkimV3 UserDeviceKeyInfoMapV3) FillInUserInfos(
newIndex int, updatedUserKeys UserDevicePublicKeys,
ePrivKey kbfscrypto.TLFEphemeralPrivateKey,
tlfCryptKey kbfscrypto.TLFCryptKey) (
serverHalves UserDeviceKeyServerHalves, err error) {
serverHalves = make(UserDeviceKeyServerHalves, len(updatedUserKeys))
for u, updatedDeviceKeys := range updatedUserKeys {
if _, ok := udkimV3[u]; !ok {
udkimV3[u] = DeviceKeyInfoMapV3{}
}
deviceServerHalves, err := udkimV3[u].fillInDeviceInfos(
u, tlfCryptKey, ePrivKey, newIndex,
updatedDeviceKeys)
if err != nil {
return nil, err
}
if len(deviceServerHalves) > 0 {
serverHalves[u] = deviceServerHalves
}
}
return serverHalves, nil
}
// All section references below are to https://keybase.io/docs/crypto/kbfs
// (version 1.8).
// TLFWriterKeyBundleV3 is a bundle of writer keys and historic
// symmetric encryption keys for a top-level folder.
type TLFWriterKeyBundleV3 struct {
// Maps from each user to their crypt key bundle for the current generation.
Keys UserDeviceKeyInfoMapV3 `codec:"wKeys"`
// M_f as described in § 4.1.1.
TLFPublicKey kbfscrypto.TLFPublicKey `codec:"pubKey"`
// M_e as described in § 4.1.1. Because devices can be added
// into the key generation after it is initially created (so
// those devices can get access to existing data), we track
// multiple ephemeral public keys; the one used by a
// particular device is specified by EPubKeyIndex in its
// TLFCryptoKeyInfo struct.
TLFEphemeralPublicKeys kbfscrypto.TLFEphemeralPublicKeys `codec:"ePubKey"`
// This is a time-ordered encrypted list of historic key generations.
// It is encrypted with the latest generation of the TLF crypt key.
EncryptedHistoricTLFCryptKeys kbfscrypto.EncryptedTLFCryptKeys `codec:"oldKeys"`
codec.UnknownFieldSetHandler
}
// DeserializeTLFWriterKeyBundleV3 deserializes a TLFWriterKeyBundleV3
// from the given path and returns it.
func DeserializeTLFWriterKeyBundleV3(codec kbfscodec.Codec, path string) (
TLFWriterKeyBundleV3, error) {
var wkb TLFWriterKeyBundleV3
err := kbfscodec.DeserializeFromFile(codec, path, &wkb)
if err != nil {
return TLFWriterKeyBundleV3{}, err
}
if len(wkb.Keys) == 0 {
return TLFWriterKeyBundleV3{}, errors.New(
"Writer key bundle with no keys (DeserializeTLFWriterKeyBundleV3)")
}
return wkb, nil
}
// Size implements the cache.Measurable interface.
func (wkb TLFWriterKeyBundleV3) Size() (bytes int) {
bytes += cache.PtrSize + wkb.Keys.Size() // Keys
// TLFPublicKey is essentially a 32-byte array.
bytes += kbfscrypto.TLFPublicKey{}.Size()
// TLFEphemeralPublicKeys
bytes += wkb.TLFEphemeralPublicKeys.Size()
// EncryptedHistoricTLFCryptKeys
bytes += wkb.EncryptedHistoricTLFCryptKeys.Size()
// For codec.UnknownFieldSetHandler. It has a private map field which we
// can't inspect unless extending the codec package. Just assume it's empty
// for now.
bytes += cache.PtrSize
return bytes
}
var _ cache.Measurable = TLFWriterKeyBundleV3{}
// IsWriter returns true if the given user device is in the device set.
func (wkb TLFWriterKeyBundleV3) IsWriter(user keybase1.UID, deviceKey kbfscrypto.CryptPublicKey) bool {
_, ok := wkb.Keys[user][deviceKey]
return ok
}
// DeepCopy creates a deep copy of this key bundle.
func (wkb TLFWriterKeyBundleV3) DeepCopy(codec kbfscodec.Codec) (
TLFWriterKeyBundleV3, error) {
if len(wkb.Keys) == 0 {
return TLFWriterKeyBundleV3{}, errors.New(
"Writer key bundle with no keys (DeepCopy)")
}
var wkbCopy TLFWriterKeyBundleV3
if err := kbfscodec.Update(codec, &wkbCopy, wkb); err != nil {
return TLFWriterKeyBundleV3{}, err
}
return wkbCopy, nil
}
// TLFWriterKeyBundleID is the hash of a serialized TLFWriterKeyBundle.
type TLFWriterKeyBundleID struct {
h kbfshash.Hash
}
var _ encoding.BinaryMarshaler = TLFWriterKeyBundleID{}
var _ encoding.BinaryUnmarshaler = (*TLFWriterKeyBundleID)(nil)
// TLFWriterKeyBundleIDFromBytes creates a new TLFWriterKeyBundleID from the given bytes.
// If the returned error is nil, the returned TLFWriterKeyBundleID is valid.
func TLFWriterKeyBundleIDFromBytes(data []byte) (TLFWriterKeyBundleID, error) {
h, err := kbfshash.HashFromBytes(data)
if err != nil {
return TLFWriterKeyBundleID{}, err
}
return TLFWriterKeyBundleID{h}, nil
}
// TLFWriterKeyBundleIDFromString creates a new TLFWriterKeyBundleID from the given string.
// If the returned error is nil, the returned TLFWriterKeyBundleID is valid.
func TLFWriterKeyBundleIDFromString(id string) (TLFWriterKeyBundleID, error) {
if len(id) == 0 {
return TLFWriterKeyBundleID{}, nil
}
h, err := kbfshash.HashFromString(id)
if err != nil {
return TLFWriterKeyBundleID{}, err
}
return TLFWriterKeyBundleID{h}, nil
}
// Bytes returns the bytes of the TLFWriterKeyBundleID.
func (h TLFWriterKeyBundleID) Bytes() []byte {
return h.h.Bytes()
}
// String returns the string form of the TLFWriterKeyBundleID.
func (h TLFWriterKeyBundleID) String() string {
return h.h.String()
}
// Size implements the cache.Measurable interface.
func (h TLFWriterKeyBundleID) Size() int {
return h.h.Size()
}
var _ cache.Measurable = TLFWriterKeyBundleID{}
// MarshalBinary implements the encoding.BinaryMarshaler interface for
// TLFWriterKeyBundleID. Returns an error if the TLFWriterKeyBundleID is invalid and not the
// zero TLFWriterKeyBundleID.
func (h TLFWriterKeyBundleID) MarshalBinary() (data []byte, err error) {
return h.h.MarshalBinary()
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
// for TLFWriterKeyBundleID. Returns an error if the given byte array is non-empty and
// the TLFWriterKeyBundleID is invalid.
func (h *TLFWriterKeyBundleID) UnmarshalBinary(data []byte) error {
return h.h.UnmarshalBinary(data)
}
// IsNil returns true if the ID is unset.
func (h TLFWriterKeyBundleID) IsNil() bool {
return h == TLFWriterKeyBundleID{}
}
// MakeTLFWriterKeyBundleID hashes a TLFWriterKeyBundleV3 to create an ID.
func MakeTLFWriterKeyBundleID(codec kbfscodec.Codec, wkb TLFWriterKeyBundleV3) (
TLFWriterKeyBundleID, error) {
if len(wkb.Keys) == 0 {
return TLFWriterKeyBundleID{}, errors.New(
"Writer key bundle with no keys (MakeTLFWriterKeyBundleID)")
}
buf, err := codec.Encode(wkb)
if err != nil {
return TLFWriterKeyBundleID{}, err
}
h, err := kbfshash.DefaultHash(buf)
if err != nil {
return TLFWriterKeyBundleID{}, err
}
return TLFWriterKeyBundleID{h}, nil
}
// TLFReaderKeyBundleV3 stores all the reader keys with reader
// permissions on a TLF.
type TLFReaderKeyBundleV3 struct {
Keys UserDeviceKeyInfoMapV3 `codec:"rKeys,omitempty"`
// M_e as described in § 4.1.1. Because devices can be added
// into the key generation after it is initially created (so
// those devices can get access to existing data), we track
// multiple ephemeral public keys; the one used by a
// particular device is specified by EPubKeyIndex in its
// TLFCryptoKeyInfo struct. This list is needed so a reader
// rekey doesn't modify the writer metadata.
TLFEphemeralPublicKeys kbfscrypto.TLFEphemeralPublicKeys `codec:"rEPubKey,omitempty"`
codec.UnknownFieldSetHandler
}
// DeserializeTLFReaderKeyBundleV3 deserializes a TLFReaderKeyBundleV3
// from the given path and returns it.
func DeserializeTLFReaderKeyBundleV3(codec kbfscodec.Codec, path string) (
TLFReaderKeyBundleV3, error) {
var rkb TLFReaderKeyBundleV3
err := kbfscodec.DeserializeFromFile(codec, path, &rkb)
if err != nil {
return TLFReaderKeyBundleV3{}, err
}
if len(rkb.Keys) == 0 {
rkb.Keys = make(UserDeviceKeyInfoMapV3)
}
return rkb, nil
}
// Size implements the cache.Measurable interface.
func (rkb TLFReaderKeyBundleV3) Size() (bytes int) {
bytes += cache.PtrSize + rkb.Keys.Size() // Keys
// TLFEphemeralPublicKeys
bytes += rkb.TLFEphemeralPublicKeys.Size()
// For codec.UnknownFieldSetHandler. It has a private map field which we
// can't inspect unless extending the codec package. Just assume it's empty
// for now.
bytes += cache.PtrSize
return bytes
}
var _ cache.Measurable = TLFReaderKeyBundleV3{}
// IsReader returns true if the given user device is in the reader set.
func (rkb TLFReaderKeyBundleV3) IsReader(user keybase1.UID, deviceKey kbfscrypto.CryptPublicKey) bool {
_, ok := rkb.Keys[user][deviceKey]
return ok
}
// DeepCopy creates a deep copy of this key bundle.
func (rkb TLFReaderKeyBundleV3) DeepCopy(codec kbfscodec.Codec) (
TLFReaderKeyBundleV3, error) {
var rkbCopy TLFReaderKeyBundleV3
if err := kbfscodec.Update(codec, &rkbCopy, rkb); err != nil {
return TLFReaderKeyBundleV3{}, err
}
if len(rkbCopy.Keys) == 0 {
rkbCopy.Keys = make(UserDeviceKeyInfoMapV3)
}
return rkbCopy, nil
}
// TLFReaderKeyBundleID is the hash of a serialized TLFReaderKeyBundle.
type TLFReaderKeyBundleID struct {
h kbfshash.Hash
}
var _ encoding.BinaryMarshaler = TLFReaderKeyBundleID{}
var _ encoding.BinaryUnmarshaler = (*TLFReaderKeyBundleID)(nil)
// TLFReaderKeyBundleIDFromBytes creates a new TLFReaderKeyBundleID from the given bytes.
// If the returned error is nil, the returned TLFReaderKeyBundleID is valid.
func TLFReaderKeyBundleIDFromBytes(data []byte) (TLFReaderKeyBundleID, error) {
h, err := kbfshash.HashFromBytes(data)
if err != nil {
return TLFReaderKeyBundleID{}, err
}
return TLFReaderKeyBundleID{h}, nil
}
// TLFReaderKeyBundleIDFromString creates a new TLFReaderKeyBundleID from the given string.
// If the returned error is nil, the returned TLFReaderKeyBundleID is valid.
func TLFReaderKeyBundleIDFromString(id string) (TLFReaderKeyBundleID, error) {
if len(id) == 0 {
return TLFReaderKeyBundleID{}, nil
}
h, err := kbfshash.HashFromString(id)
if err != nil {
return TLFReaderKeyBundleID{}, err
}
return TLFReaderKeyBundleID{h}, nil
}
// Bytes returns the bytes of the TLFReaderKeyBundleID.
func (h TLFReaderKeyBundleID) Bytes() []byte {
return h.h.Bytes()
}
// String returns the string form of the TLFReaderKeyBundleID.
func (h TLFReaderKeyBundleID) String() string {
return h.h.String()
}
// MarshalBinary implements the encoding.BinaryMarshaler interface for
// TLFReaderKeyBundleID. Returns an error if the TLFReaderKeyBundleID is invalid and not the
// zero TLFReaderKeyBundleID.
func (h TLFReaderKeyBundleID) MarshalBinary() (data []byte, err error) {
return h.h.MarshalBinary()
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
// for TLFReaderKeyBundleID. Returns an error if the given byte array is non-empty and
// the TLFReaderKeyBundleID is invalid.
func (h *TLFReaderKeyBundleID) UnmarshalBinary(data []byte) error {
return h.h.UnmarshalBinary(data)
}
// IsNil returns true if the ID is unset.
func (h TLFReaderKeyBundleID) IsNil() bool {
return h == TLFReaderKeyBundleID{}
}
// MakeTLFReaderKeyBundleID hashes a TLFReaderKeyBundleV3 to create an ID.
func MakeTLFReaderKeyBundleID(codec kbfscodec.Codec, rkb TLFReaderKeyBundleV3) (
TLFReaderKeyBundleID, error) {
buf, err := codec.Encode(rkb)
if err != nil {
return TLFReaderKeyBundleID{}, err
}
h, err := kbfshash.DefaultHash(buf)
if err != nil {
return TLFReaderKeyBundleID{}, err
}
return TLFReaderKeyBundleID{h}, nil
}
// Size implements the cache.Measurable interface.
func (h TLFReaderKeyBundleID) Size() int {
return h.h.Size()
}
var _ cache.Measurable = TLFReaderKeyBundleID{}