/
per_user_key.go
745 lines (633 loc) · 22.2 KB
/
per_user_key.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
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
package libkb
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"sort"
"strings"
"sync"
keybase1 "github.com/keybase/client/go/protocol/keybase1"
"github.com/keybase/go-codec/codec"
"golang.org/x/crypto/nacl/secretbox"
context "golang.org/x/net/context"
)
const PerUserKeySeedSize = 32
// A secretbox containg a seed encrypted for its successor generation
type PerUserKeyPrev string
type PerUserKeySeed [PerUserKeySeedSize]byte
func (s *PerUserKeySeed) DeriveSigningKey() (*NaclSigningKeyPair, error) {
derived, err := DeriveFromSecret(*s, DeriveReasonPUKSigning)
if err != nil {
return nil, err
}
res, err := MakeNaclSigningKeyPairFromSecret(derived)
return &res, err
}
func (s *PerUserKeySeed) DeriveDHKey() (*NaclDHKeyPair, error) {
derived, err := DeriveFromSecret(*s, DeriveReasonPUKEncryption)
if err != nil {
return nil, err
}
res, err := MakeNaclDHKeyPairFromSecret(derived)
return &res, err
}
// derivePrevKey derives the symmetric key used to secretbox the previous generation seed.
func (s *PerUserKeySeed) derivePrevKey() (res NaclSecretBoxKey, err error) {
derived, err := DeriveFromSecret(*s, DeriveReasonPUKPrev)
if err != nil {
return res, err
}
return NaclSecretBoxKey(derived), err
}
func (s *PerUserKeySeed) IsBlank() bool {
var blank PerUserKeySeed
return (subtle.ConstantTimeCompare(s[:], blank[:]) == 1)
}
func NewPerUserKeyBox(contents PerUserKeySeed, receiverKey NaclDHKeyPair, senderKey NaclDHKeyPair, generation keybase1.PerUserKeyGeneration) (keybase1.PerUserKeyBox, error) {
if contents.IsBlank() {
return keybase1.PerUserKeyBox{}, errors.New("attempt to box blank per-user-key")
}
encInfo, err := receiverKey.Encrypt(contents[:], &senderKey)
if err != nil {
return keybase1.PerUserKeyBox{}, err
}
boxStr, err := PacketArmoredEncode(encInfo)
if err != nil {
return keybase1.PerUserKeyBox{}, err
}
return keybase1.PerUserKeyBox{
Box: boxStr,
ReceiverKID: receiverKey.GetKID(),
Generation: generation,
}, nil
}
// Returns base64 of a msgpack array of 3 items: [version, nonce, box]
// Does not do any derivation steps. Caller should pass symmetricKey derived with pukContextPrev.
func newPerUserKeyPrev(contents PerUserKeySeed, symmetricKey NaclSecretBoxKey) (PerUserKeyPrev, error) {
if contents.IsBlank() {
return "", errors.New("attempt to secretbox blank per-user-key")
}
const version = 1
var nonce [NaclDHNonceSize]byte
if nRead, err := rand.Read(nonce[:]); err != nil {
return "", err
} else if nRead != NaclDHNonceSize {
return "", fmt.Errorf("Short random read: %d", nRead)
}
// secretbox
sealed := secretbox.Seal(nil, contents[:], &nonce, (*[NaclSecretBoxKeySize]byte)(&symmetricKey))
parts := []interface{}{version, nonce, sealed}
// msgpack
mh := codec.MsgpackHandle{WriteExt: true}
var msgpacked []byte
enc := codec.NewEncoderBytes(&msgpacked, &mh)
err := enc.Encode(parts)
if err != nil {
return "", err
}
// b64
return PerUserKeyPrev(base64.StdEncoding.EncodeToString(msgpacked)), nil
}
// Opens the output of NewPerUserKeyPrev
func openPerUserKeyPrev(sbox PerUserKeyPrev, symmetricKey NaclSecretBoxKey) (PerUserKeySeed, error) {
var res PerUserKeySeed
// decode b64
msgpacked, err := base64.StdEncoding.DecodeString(string(sbox))
if err != nil {
return res, err
}
// decode msgpack
mh := codec.MsgpackHandle{WriteExt: true}
dec := codec.NewDecoderBytes(msgpacked, &mh)
var parts struct {
Version int
Nonce []byte
Sealed []byte
}
err = dec.Decode(&parts)
if err != nil {
return res, err
}
// check parts
if parts.Version != 1 {
return res, fmt.Errorf("per user key secret box version %v != 1", parts.Version)
}
if len(parts.Nonce) != NaclDHNonceSize {
return res, fmt.Errorf("per user key secret box nonce length %v != %v",
len(parts.Nonce), NaclDHNonceSize)
}
var nonce [NaclDHNonceSize]byte
copy(nonce[:], parts.Nonce)
expectedSealedLength := PerUserKeySeedSize + secretbox.Overhead
if len(parts.Sealed) != expectedSealedLength {
return res, fmt.Errorf("per user key secret box sealed length %v != %v", len(parts.Sealed), expectedSealedLength)
}
// open secretbox
var symmetricKey2 [NaclSecretBoxKeySize]byte = symmetricKey
contents, ok := secretbox.Open(nil, parts.Sealed, &nonce, &symmetricKey2)
if !ok {
return res, errors.New("per user key secret box open failed")
}
if len(contents) != PerUserKeySeedSize {
return res, fmt.Errorf("per user key seed length %v != %v", len(contents), PerUserKeySeedSize)
}
return MakeByte32(contents), nil
}
type perUserKeyFull struct {
seed PerUserKeySeed
sigKey *NaclSigningKeyPair
encKey *NaclDHKeyPair
}
type perUserKeyMap map[keybase1.PerUserKeyGeneration]perUserKeyFull
type perUserKeySeqGenMap map[keybase1.Seqno]keybase1.PerUserKeyGeneration
// PerUserKeyring holds on to all versions of the per user key.
// Generation=0 should be nil, but all others should be present.
type PerUserKeyring struct {
Contextified
sync.Mutex
uid keybase1.UID
generations perUserKeyMap
// sigchain seqno when each key was introduced
seqgen perUserKeySeqGenMap
}
// NewPerUserKeyring makes a new per-user-key keyring for a given UID.
func NewPerUserKeyring(g *GlobalContext, uid keybase1.UID) (*PerUserKeyring, error) {
if uid.IsNil() {
return nil, fmt.Errorf("NewPerUserKeyring called with nil uid")
}
return &PerUserKeyring{
Contextified: NewContextified(g),
uid: uid,
generations: make(perUserKeyMap),
seqgen: make(perUserKeySeqGenMap),
}, nil
}
func (s *PerUserKeyring) GetUID() keybase1.UID {
return s.uid
}
// PrepareBoxForNewDevice encrypts the latest shared key seed for a new device.
// The returned box should be pushed to the server.
func (s *PerUserKeyring) PrepareBoxForNewDevice(ctx context.Context, receiverKey NaclDHKeyPair,
senderKey NaclDHKeyPair) (box keybase1.PerUserKeyBox, err error) {
s.Lock()
defer s.Unlock()
gen := s.currentGenerationLocked()
if gen < 1 {
return box, errors.New("PerUserKeyring#PrepareBoxForNewDevice no keys loaded")
}
full, ok := s.generations[gen]
if !ok {
return box, errors.New("PerUserKeyring#PrepareBoxForNewDevice missing entry for current generation")
}
box, err = NewPerUserKeyBox(full.seed, receiverKey, senderKey, gen)
return box, err
}
// Encrypt seed for receiverKeys. Use senderKey to encrypt.
// Does not use the keyring at all. Attached for organizational purposes.
// Used when creating a new seed.
func (s *PerUserKeyring) PrepareBoxesForDevices(ctx context.Context, contents PerUserKeySeed,
generation keybase1.PerUserKeyGeneration, receiverKeys []NaclDHKeyPair,
senderKey GenericKey) (boxes []keybase1.PerUserKeyBox, err error) {
// Do not lock self because we do not use self.
if contents.IsBlank() {
return nil, errors.New("attempt to box blank per-user-key")
}
senderKeyDH, ok := senderKey.(NaclDHKeyPair)
if !ok {
return nil, fmt.Errorf("Unexpected encryption key type: %T", senderKey)
}
for _, receiverKey := range receiverKeys {
box, err := NewPerUserKeyBox(contents, receiverKey, senderKeyDH, generation)
if err != nil {
return nil, err
}
boxes = append(boxes, box)
}
return boxes, nil
}
// Prepares a prev secretbox containing generation n-1 encrypted for generation n.
// Asserts that the current generation is n-1.
// The `generation` parameter is n.
func (s *PerUserKeyring) PreparePrev(ctx context.Context, newSeed PerUserKeySeed,
newGeneration keybase1.PerUserKeyGeneration) (PerUserKeyPrev, error) {
s.Lock()
defer s.Unlock()
if newSeed.IsBlank() {
return "", errors.New("attempt to prev with blank per-user-key")
}
currentGen := s.currentGenerationLocked()
if currentGen == 0 {
return "", errors.New("attempt to prepare per-user-key prev with no keys")
}
if currentGen != newGeneration-1 {
return "", fmt.Errorf("incompatible prev generations %v != %v-1", currentGen, newGeneration)
}
symmetricKey, err := newSeed.derivePrevKey()
if err != nil {
return "", err
}
contents := s.generations[currentGen].seed
return newPerUserKeyPrev(contents, symmetricKey)
}
// AddKey registers a full key locally.
func (s *PerUserKeyring) AddKey(ctx context.Context, generation keybase1.PerUserKeyGeneration,
seqno keybase1.Seqno, seed PerUserKeySeed) error {
s.Lock()
defer s.Unlock()
s.G().Log.CDebugf(ctx, "PerUserKeyring#AddKey(generation: %v, seqno:%v)", generation, seqno)
if seed.IsBlank() {
return errors.New("attempt to add blank per-user-key")
}
currentGen := s.currentGenerationLocked()
if generation != currentGen+1 {
return fmt.Errorf("cannot add key for non-next generation: %v != %v+1",
generation, currentGen)
}
_, exists := s.generations[generation]
if exists {
return fmt.Errorf("AddKey duplicate for generation: %v", generation)
}
expanded, err := expandPerUserKey(seed)
if err != nil {
return err
}
s.generations[generation] = expanded.lower()
s.seqgen[seqno] = generation
return nil
}
func (s *PerUserKeyring) HasAnyKeys() bool {
return s.CurrentGeneration() > 0
}
// CurrentGeneration returns what generation we're on. The version possible
// Version is 1. Version 0 implies no keys are available.
func (s *PerUserKeyring) CurrentGeneration() keybase1.PerUserKeyGeneration {
s.Lock()
defer s.Unlock()
return s.currentGenerationLocked()
}
func (s *PerUserKeyring) currentGenerationLocked() keybase1.PerUserKeyGeneration {
return keybase1.PerUserKeyGeneration(len(s.generations))
}
func (s *PerUserKeyring) GetLatestSigningKey(ctx context.Context) (*NaclSigningKeyPair, error) {
s.Lock()
defer s.Unlock()
gen := s.currentGenerationLocked()
if gen < 1 {
return nil, errors.New("PerUserKeyring#GetLatestSigningKey no keys loaded")
}
key, found := s.generations[gen]
if !found {
return nil, fmt.Errorf("PerUserKeyring#GetLatestSigningKey no key for generation %v", gen)
}
return key.sigKey, nil
}
// Get the encryption key of a generation.
func (s *PerUserKeyring) GetEncryptionKeyByGeneration(ctx context.Context, gen keybase1.PerUserKeyGeneration) (*NaclDHKeyPair, error) {
s.Lock()
defer s.Unlock()
return s.getEncryptionKeyByGenerationLocked(ctx, gen)
}
func (s *PerUserKeyring) getEncryptionKeyByGenerationLocked(ctx context.Context, gen keybase1.PerUserKeyGeneration) (*NaclDHKeyPair, error) {
if gen < 1 {
return nil, fmt.Errorf("PerUserKeyring#GetEncryptionKey bad generation number %v", gen)
}
key, found := s.generations[gen]
if !found {
return nil, fmt.Errorf("no encryption key for generation %v", gen)
}
return key.encKey, nil
}
// Get the encryption key at the user sigchain seqno.
func (s *PerUserKeyring) GetEncryptionKeyBySeqno(ctx context.Context, seqno keybase1.Seqno) (*NaclDHKeyPair, error) {
s.Lock()
defer s.Unlock()
gen, ok := s.seqgen[seqno]
if !ok {
return nil, fmt.Errorf("no encrypted key for seqno %v", seqno)
}
return s.getEncryptionKeyByGenerationLocked(ctx, gen)
}
// GetEncryptionKeyByKID finds an encryption key that matches kid.
func (s *PerUserKeyring) GetEncryptionKeyByKID(ctx context.Context, kid keybase1.KID) (*NaclDHKeyPair, error) {
s.Lock()
defer s.Unlock()
for _, key := range s.generations {
if key.encKey.GetKID().Equal(kid) {
return key.encKey, nil
}
}
return nil, NotFoundError{Msg: fmt.Sprintf("no per-user encryption key found for KID %s", kid)}
}
// Sync our PerUserKeyring with the server. It will either add all new
// keys since our last update, or not at all if there was an error.
// Pass it a standard Go network context.
func (s *PerUserKeyring) Sync(ctx context.Context) (err error) {
return s.syncAsConfiguredDevice(ctx, nil, nil)
}
// `lctx` and `upak` are optional
func (s *PerUserKeyring) SyncWithExtras(ctx context.Context, lctx LoginContext, upak *keybase1.UserPlusAllKeys) (err error) {
return s.syncAsConfiguredDevice(ctx, lctx, upak)
}
// `lctx` and `upak` are optional
func (s *PerUserKeyring) syncAsConfiguredDevice(ctx context.Context, lctx LoginContext, upak *keybase1.UserPlusAllKeys) (err error) {
uid, deviceID, _, _, activeDecryptionKey := s.G().ActiveDevice.AllFields()
if !s.uid.Equal(uid) {
return fmt.Errorf("UID changed on PerUserKeyring")
}
if deviceID.IsNil() {
return fmt.Errorf("missing configured deviceID")
}
return s.sync(ctx, lctx, upak, deviceID, activeDecryptionKey)
}
// `lctx` and `upak` are optional
func (s *PerUserKeyring) SyncAsPaperKey(ctx context.Context, lctx LoginContext, upak *keybase1.UserPlusAllKeys, deviceID keybase1.DeviceID, decryptionKey GenericKey) (err error) {
if deviceID.IsNil() {
return fmt.Errorf("missing deviceID")
}
// Note this `== nil` check might not work, as it might be a typed nil.
if decryptionKey == nil {
return fmt.Errorf("missing decryption key")
}
return s.sync(ctx, lctx, upak, deviceID, decryptionKey)
}
// `lctx` and `upak` are optional
func (s *PerUserKeyring) sync(ctx context.Context, lctx LoginContext, upak *keybase1.UserPlusAllKeys, deviceID keybase1.DeviceID, decryptionKey GenericKey) (err error) {
defer s.G().CTrace(ctx, "PerUserKeyring#sync", func() error { return err })()
s.G().Log.CDebugf(ctx, "PerUserKeyring#sync(%v, %v)", lctx != nil, upak != nil)
s.Lock()
defer s.Unlock()
box, prevs, err := s.fetchBoxesLocked(ctx, lctx, deviceID)
if err != nil {
return err
}
if upak == nil {
upak, err = s.getUPAK(ctx, lctx, upak)
if err != nil {
return err
}
}
checker := newPerUserKeyChecker(upak)
newKeys, err := s.importLocked(ctx, box, prevs, decryptionKey, checker)
if err != nil {
return err
}
s.mergeLocked(newKeys, checker.seqgen)
return nil
}
func (s *PerUserKeyring) getUPAK(ctx context.Context, lctx LoginContext, upak *keybase1.UserPlusAllKeys) (*keybase1.UserPlusAllKeys, error) {
if upak != nil {
return upak, nil
}
upakArg := NewLoadUserByUIDArg(ctx, s.G(), s.uid).WithLoginContext(lctx)
upak, _, err := s.G().GetUPAKLoader().Load(upakArg)
return upak, err
}
func (s *PerUserKeyring) mergeLocked(m perUserKeyMap, seqgen perUserKeySeqGenMap) (err error) {
for k, v := range m {
s.generations[k] = v
}
s.seqgen = seqgen
return nil
}
type perUserKeySyncResp struct {
Box *keybase1.PerUserKeyBox `json:"box"`
Prevs []perUserKeyPrevResp `json:"prevs"`
Status AppStatus `json:"status"`
}
func (s *perUserKeySyncResp) GetAppStatus() *AppStatus {
return &s.Status
}
type perUserKeyPrevResp struct {
Ctext PerUserKeyPrev `json:"ctext"`
Generation keybase1.PerUserKeyGeneration `json:"generation"`
}
type byGeneration []perUserKeyPrevResp
func (m byGeneration) Len() int { return len(m) }
func (m byGeneration) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m byGeneration) Less(i, j int) bool { return m[i].Generation < m[j].Generation }
func (s *PerUserKeyring) fetchBoxesLocked(ctx context.Context, lctx LoginContext,
deviceID keybase1.DeviceID) (box *keybase1.PerUserKeyBox, prevs []perUserKeyPrevResp, err error) {
defer s.G().CTrace(ctx, "PerUserKeyring#fetchBoxesLocked", func() error { return err })()
var sessionR SessionReader
if lctx != nil {
sessionR = lctx.LocalSession()
}
var resp perUserKeySyncResp
err = s.G().API.GetDecode(APIArg{
Endpoint: "key/fetch_per_user_key_secrets",
Args: HTTPArgs{
"generation": I{int(s.currentGenerationLocked())},
"device_id": S{deviceID.String()},
},
SessionType: APISessionTypeREQUIRED,
SessionR: sessionR,
RetryCount: 5, // It's pretty bad to fail this, so retry.
NetContext: ctx,
}, &resp)
if err != nil {
return nil, nil, err
}
s.G().Log.CDebugf(ctx, "| Got back box:%v and prevs:%d from server", resp.Box != nil, len(resp.Prevs))
return resp.Box, resp.Prevs, nil
}
// perUserKeyChecker checks the [secret]boxes returned from the server
// against the public keys advertised in the user's sigchain. As we import
// keys, we check them. We check that the boxes were encryted with a
// valid device subkey (though it can be now revoked). And we check that the
// public keys corresponds to what was signed in as a per_user_key.
type perUserKeyChecker struct {
allowedEncryptingKIDs map[keybase1.KID]bool
expectedPUKSigKIDs map[keybase1.PerUserKeyGeneration]keybase1.KID
expectedPUKEncKIDs map[keybase1.PerUserKeyGeneration]keybase1.KID
latestGeneration keybase1.PerUserKeyGeneration
seqgen perUserKeySeqGenMap
}
func newPerUserKeyChecker(upak *keybase1.UserPlusAllKeys) *perUserKeyChecker {
ret := perUserKeyChecker{
allowedEncryptingKIDs: make(map[keybase1.KID]bool),
expectedPUKSigKIDs: make(map[keybase1.PerUserKeyGeneration]keybase1.KID),
expectedPUKEncKIDs: make(map[keybase1.PerUserKeyGeneration]keybase1.KID),
latestGeneration: 0,
seqgen: make(perUserKeySeqGenMap),
}
isEncryptionKey := func(k keybase1.PublicKey) bool {
return !k.IsSibkey && k.PGPFingerprint == ""
}
for _, r := range upak.Base.RevokedDeviceKeys {
if isEncryptionKey(r.Key) {
ret.allowedEncryptingKIDs[r.Key.KID] = true
}
}
for _, k := range upak.Base.DeviceKeys {
if isEncryptionKey(k) {
ret.allowedEncryptingKIDs[k.KID] = true
}
}
for _, k := range upak.Base.PerUserKeys {
ret.expectedPUKSigKIDs[keybase1.PerUserKeyGeneration(k.Gen)] = k.SigKID
ret.expectedPUKEncKIDs[keybase1.PerUserKeyGeneration(k.Gen)] = k.EncKID
gen := keybase1.PerUserKeyGeneration(k.Gen)
ret.seqgen[k.Seqno] = gen
if gen > ret.latestGeneration {
ret.latestGeneration = gen
}
}
return &ret
}
// checkPublic checks that a key matches the KIDs published by the user.
func (c *perUserKeyChecker) checkPublic(key importedPerUserKey, generation keybase1.PerUserKeyGeneration) error {
// sig key
if expectedSigKID, ok := c.expectedPUKSigKIDs[generation]; ok {
if !expectedSigKID.SecureEqual(key.sigKey.GetKID()) {
return fmt.Errorf("import per-user-key: wrong sigKID expected %v", expectedSigKID.String())
}
} else {
return fmt.Errorf("import per-user-key: no sigKID for generation: %v", generation)
}
// enc key
if expectedEncKID, ok := c.expectedPUKEncKIDs[generation]; ok {
if !expectedEncKID.SecureEqual(key.encKey.GetKID()) {
return fmt.Errorf("import per-user-key: wrong sigKID expected %v", expectedEncKID.String())
}
} else {
return fmt.Errorf("import per-user-key: no sigKID for generation: %v", generation)
}
return nil
}
func (s *PerUserKeyring) importLocked(ctx context.Context,
box *keybase1.PerUserKeyBox, prevs []perUserKeyPrevResp,
decryptionKey GenericKey, checker *perUserKeyChecker) (ret perUserKeyMap, err error) {
defer s.G().CTrace(ctx, "PerUserKeyring#importLocked", func() error { return err })()
if box == nil && len(prevs) == 0 {
// No new stuff, this keyring is up to date.
return make(perUserKeyMap), nil
}
if box == nil {
return nil, errors.New("per-user-key import nil box")
}
if box.Generation != checker.latestGeneration {
return nil, fmt.Errorf("sync (%v) and checker (%v) disagree on generation", box.Generation, checker.latestGeneration)
}
sort.Sort(sort.Reverse(byGeneration(prevs)))
var debugPrevGenList []string
for _, prev := range prevs {
debugPrevGenList = append(debugPrevGenList, fmt.Sprintf("%d", prev.Generation))
}
if len(debugPrevGenList) > 0 {
s.G().Log.CDebugf(ctx, "PerUserKeyring#importLocked prevs:(%s)", strings.Join(debugPrevGenList, ","))
}
ret = make(perUserKeyMap)
imp1, err := importPerUserKeyBox(box, decryptionKey, checker.latestGeneration, checker)
if err != nil {
return nil, err
}
ret[box.Generation] = imp1.lower()
walkGeneration := box.Generation - 1
walkKey := imp1.prevKey
for _, prev := range prevs {
if walkGeneration <= 0 {
return nil, errors.New("per-user-key prev chain too long")
}
imp, err := importPerUserKeyPrev(walkGeneration, prev, walkKey, walkGeneration, checker)
if err != nil {
return nil, err
}
ret[walkGeneration] = imp.lower()
walkGeneration--
walkKey = imp.prevKey
}
return ret, nil
}
type importedPerUserKey struct {
seed PerUserKeySeed
sigKey *NaclSigningKeyPair
encKey *NaclDHKeyPair
// Key used to decrypt the secretbox of the previous generation
prevKey NaclSecretBoxKey
}
func (k *importedPerUserKey) lower() perUserKeyFull {
return perUserKeyFull{
seed: k.seed,
sigKey: k.sigKey,
encKey: k.encKey,
}
}
// Decrypt, expand, and check a per-user-key from a Box.
func importPerUserKeyBox(box *keybase1.PerUserKeyBox, decryptionKey GenericKey,
wantedGeneration keybase1.PerUserKeyGeneration, checker *perUserKeyChecker) (*importedPerUserKey, error) {
if box == nil {
return nil, NewPerUserKeyImportError("per-user-key box nil")
}
if box.Generation != wantedGeneration {
return nil, NewPerUserKeyImportError("bad generation returned: %d", box.Generation)
}
if !decryptionKey.GetKID().Equal(box.ReceiverKID) {
return nil, NewPerUserKeyImportError("wrong encryption kid: %s", box.ReceiverKID.String())
}
rawKey, encryptingKID, err := decryptionKey.DecryptFromString(box.Box)
if err != nil {
return nil, err
}
if len(checker.allowedEncryptingKIDs) == 0 {
return nil, NewPerUserKeyImportError("no allowed encrypting kids")
}
if !checker.allowedEncryptingKIDs[encryptingKID] {
return nil, NewPerUserKeyImportError("unexpected encrypting kid: %s", encryptingKID)
}
seed, err := MakeByte32Soft(rawKey)
if err != nil {
return nil, NewPerUserKeyImportError("%s", err)
}
imp, err := expandPerUserKey(seed)
if err != nil {
return nil, NewPerUserKeyImportError("%s", err)
}
err = checker.checkPublic(imp, wantedGeneration)
if err != nil {
return nil, err
}
return &imp, nil
}
// Decrypt, expand, and check a per-user-key from a SecretBox.
func importPerUserKeyPrev(generation keybase1.PerUserKeyGeneration, prev perUserKeyPrevResp, decryptionKey NaclSecretBoxKey,
wantedGeneration keybase1.PerUserKeyGeneration, checker *perUserKeyChecker) (*importedPerUserKey, error) {
if generation != prev.Generation {
return nil, fmt.Errorf("import per-user-key mismatched generation: %v != %v",
generation, prev.Generation)
}
seed, err := openPerUserKeyPrev(prev.Ctext, decryptionKey)
if err != nil {
return nil, err
}
imp, err := expandPerUserKey(seed)
if err != nil {
return nil, err
}
err = checker.checkPublic(imp, generation)
if err != nil {
return nil, err
}
return &imp, nil
}
func expandPerUserKey(seed PerUserKeySeed) (res importedPerUserKey, err error) {
sigKey, err := seed.DeriveSigningKey()
if err != nil {
return res, err
}
encKey, err := seed.DeriveDHKey()
if err != nil {
return res, err
}
prevKey, err := seed.derivePrevKey()
if err != nil {
return res, err
}
return importedPerUserKey{
seed: seed,
sigKey: sigKey,
encKey: encKey,
prevKey: prevKey,
}, nil
}