1package teams
2
3import (
4	"crypto/hmac"
5	"crypto/rand"
6	"crypto/sha512"
7	"encoding/base64"
8	"errors"
9	"fmt"
10
11	"github.com/keybase/client/go/libkb"
12	"github.com/keybase/client/go/msgpack"
13	"github.com/keybase/client/go/protocol/keybase1"
14	"golang.org/x/crypto/nacl/box"
15	"golang.org/x/crypto/nacl/secretbox"
16	"golang.org/x/net/context"
17)
18
19// Get a PTK seed and verify against the sigchain that is the correct key.
20func GetAndVerifyPerTeamKey(mctx libkb.MetaContext, team Teamer, gen keybase1.PerTeamKeyGeneration) (ret keybase1.PerTeamKeySeedItem, err error) {
21
22	if team.MainChain() == nil {
23		return ret, libkb.NotFoundError{Msg: fmt.Sprintf("no team secret found at generation %v, since inner team was nil", gen)}
24	}
25
26	var ok bool
27	ret, ok = team.MainChain().PerTeamKeySeedsUnverified[gen]
28	if !ok {
29		return ret, libkb.NotFoundError{
30			Msg: fmt.Sprintf("no team secret found at generation %v", gen)}
31	}
32	km, err := NewTeamKeyManagerWithSeedItem(team.MainChain().ID(), ret)
33	if err != nil {
34		return ret, err
35	}
36
37	chainKey, err := newTeamSigChainState(team).GetPerTeamKeyAtGeneration(gen)
38	if err != nil {
39		return ret, err
40	}
41
42	// Takes roughly 280us on android (Honor 6X)
43	localSigKey, err := km.SigningKey()
44	if err != nil {
45		return ret, err
46	}
47
48	// Takes roughly 900us on android (Honor 6X)
49	localEncKey, err := km.EncryptionKey()
50	if err != nil {
51		return ret, err
52	}
53
54	if !chainKey.SigKID.SecureEqual(localSigKey.GetKID()) {
55		mctx.Debug("sig KID gen:%v (local) %v != %v (chain)", gen, localSigKey.GetKID(), chainKey.SigKID)
56		return ret, fmt.Errorf("wrong team key found at generation %v", gen)
57	}
58
59	if !chainKey.EncKID.SecureEqual(localEncKey.GetKID()) {
60		mctx.Debug("enc KID gen:%v (local) %v != %v (chain)", gen, localEncKey.GetKID(), chainKey.EncKID)
61		return ret, fmt.Errorf("wrong team key (enc) found at generation %v", gen)
62	}
63
64	return ret, nil
65}
66
67type PerTeamSharedSecretBoxes struct {
68	Generation       keybase1.PerTeamKeyGeneration `json:"generation"`
69	EncryptingKid    keybase1.KID                  `json:"encrypting_kid"`
70	Nonce            string                        `json:"nonce"`
71	PrevKey          *prevKeySealedEncoded         `json:"prev"`
72	Boxes            map[keybase1.UID]string       `json:"boxes"`
73	BoxSummaryHash   string                        `json:"public_summary"` // encoded hash of the packed box public summary
74	boxPublicSummary *boxPublicSummary             // not exported, therefore, won't be JSON'ed
75}
76
77type PerTeamSharedSecretBox struct {
78	_struct         bool `codec:",toarray"` //nolint
79	Version         uint
80	PerUserKeySeqno keybase1.Seqno
81	NonceCounter    uint32
82	Ctext           []byte
83}
84
85type TeamKeyManager struct {
86	sharedSecret keybase1.PerTeamKeySeed
87	generation   keybase1.PerTeamKeyGeneration
88	check        keybase1.PerTeamSeedCheck
89	id           keybase1.TeamID
90
91	encryptionKey *libkb.NaclDHKeyPair
92	signingKey    *libkb.NaclSigningKeyPair
93}
94
95func NewTeamKeyManager(g *libkb.GlobalContext, id keybase1.TeamID) (*TeamKeyManager, error) {
96	sharedSecret, err := newSharedSecret()
97	if err != nil {
98		return nil, err
99	}
100	check, err := computeSeedCheck(id, sharedSecret, nil)
101	if err != nil {
102		return nil, err
103	}
104	return NewTeamKeyManagerWithSecret(id, sharedSecret, 1, check)
105}
106
107func NewTeamKeyManagerWithSeedItem(id keybase1.TeamID, si keybase1.PerTeamKeySeedItem) (*TeamKeyManager, error) {
108	return NewTeamKeyManagerWithSecret(id, si.Seed, si.Generation, si.Check)
109}
110
111func NewTeamKeyManagerWithSecret(id keybase1.TeamID, secret keybase1.PerTeamKeySeed, generation keybase1.PerTeamKeyGeneration, check *keybase1.PerTeamSeedCheck) (*TeamKeyManager, error) {
112	if check == nil {
113		return nil, fmt.Errorf("unexpected nil check item")
114	}
115	return &TeamKeyManager{
116		sharedSecret: secret,
117		generation:   generation,
118		check:        *check,
119		id:           id,
120	}, nil
121}
122
123// SharedSecret returns the team's shared secret.
124func (t *TeamKeyManager) SharedSecret() keybase1.PerTeamKeySeed {
125	return t.sharedSecret
126}
127
128// EncryptionKey returns the derived NaclSigningKeyPair from the team's shared secret.
129func (t *TeamKeyManager) SigningKey() (libkb.NaclSigningKeyPair, error) {
130	if t.signingKey == nil {
131		key, err := libkb.MakeNaclSigningKeyPairFromSecretBytes(derivedSecret(t.sharedSecret, libkb.TeamEdDSADerivationString))
132		if err != nil {
133			return libkb.NaclSigningKeyPair{}, err
134		}
135		t.signingKey = &key
136	}
137	return *t.signingKey, nil
138}
139
140func (t *TeamKeyManager) Check() keybase1.PerTeamSeedCheck {
141	return t.check
142}
143
144func (t *TeamKeyManager) Generation() keybase1.PerTeamKeyGeneration {
145	return t.generation
146}
147
148// EncryptionKey returns the derived NaclDHKeyPair from the team's shared secret.
149func (t *TeamKeyManager) EncryptionKey() (libkb.NaclDHKeyPair, error) {
150	if t.encryptionKey == nil {
151		key, err := libkb.MakeNaclDHKeyPairFromSecretBytes(derivedSecret(t.sharedSecret, libkb.TeamDHDerivationString))
152		if err != nil {
153			return libkb.NaclDHKeyPair{}, err
154		}
155		t.encryptionKey = &key
156	}
157	return *t.encryptionKey, nil
158}
159
160// SharedSecretBoxes creates the PerTeamSharedSecretBoxes for recipients with the
161// existing team shared secret.
162func (t *TeamKeyManager) SharedSecretBoxes(mctx libkb.MetaContext, senderKey libkb.GenericKey, recipients map[keybase1.UserVersion]keybase1.PerUserKey) (boxes *PerTeamSharedSecretBoxes, err error) {
163	defer mctx.Trace("SharedSecretBoxes", &err)()
164
165	// make the nonce prefix, skipping the zero counter
166	// (0 used for previous key encryption nonce)
167	n, err := newNonce24SkipZero()
168	if err != nil {
169		return nil, err
170	}
171
172	// make the recipient boxes with the new secret and the nonce prefix
173	return t.sharedBoxes(t.sharedSecret, t.generation, n, senderKey, recipients)
174}
175
176// RotateSharedSecretBoxes creates a new shared secret for the team and the
177// required PerTeamKey section.
178func (t *TeamKeyManager) RotateSharedSecretBoxes(mctx libkb.MetaContext, senderKey libkb.GenericKey, recipients map[keybase1.UserVersion]keybase1.PerUserKey) (boxes *PerTeamSharedSecretBoxes, keySection *SCPerTeamKey, err error) {
179	defer mctx.Trace("RotateSharedSecretBoxes", &err)()
180
181	// make a new secret
182	nextSecret, err := newSharedSecret()
183	if err != nil {
184		return nil, nil, err
185	}
186
187	// derive new key from new secret for PrevKey
188	key := derivedSecret(nextSecret, libkb.TeamPrevKeySecretBoxDerivationString)
189	var keyb [32]byte
190	copy(keyb[:], key)
191
192	// encrypt existing secret with derived key and nonce counter 0
193	nonce, err := newNonce24()
194	if err != nil {
195		return nil, nil, err
196	}
197	nonceBytes, counter := nonce.Nonce()
198	if counter != 0 {
199		// this should never happen, but might as well make sure it is zero
200		return nil, nil, errors.New("nonce counter not 0 for first use")
201	}
202	sealed := secretbox.Seal(nil, t.sharedSecret.ToBytes(), &nonceBytes, &keyb)
203
204	// encode encrypted prev key
205	prevKeyEncoded, err := encodeSealedPrevKey(nonceBytes, sealed)
206	if err != nil {
207		return nil, nil, err
208	}
209
210	// make the recipient boxes with the new secret and the incrementing nonce24
211	err = t.setNextSharedSecret(mctx, nextSecret)
212	if err != nil {
213		return nil, nil, err
214	}
215	boxes, err = t.sharedBoxes(t.sharedSecret, t.generation, nonce, senderKey, recipients)
216	if err != nil {
217		return nil, nil, err
218	}
219
220	// insert encoded encrypted PrevKey
221	boxes.PrevKey = &prevKeyEncoded
222
223	// need a new PerTeamKey section since the key was rotated
224	keySection, err = t.perTeamKeySection()
225	if err != nil {
226		return nil, nil, err
227	}
228
229	return boxes, keySection, nil
230}
231
232func (t *TeamKeyManager) sharedBoxes(secret keybase1.PerTeamKeySeed, generation keybase1.PerTeamKeyGeneration, nonce *nonce24, senderKey libkb.GenericKey, recipients map[keybase1.UserVersion]keybase1.PerUserKey) (*PerTeamSharedSecretBoxes, error) {
233	senderNaclDHKey, ok := senderKey.(libkb.NaclDHKeyPair)
234	if !ok {
235		return nil, fmt.Errorf("got an unexpected key type for device encryption key: %T", senderKey)
236	}
237
238	boxes, err := t.recipientBoxes(secret, nonce, senderNaclDHKey, recipients)
239	if err != nil {
240		return nil, err
241	}
242	boxPublicSummary, err := newBoxPublicSummary(recipients)
243	if err != nil {
244		return nil, err
245	}
246
247	return &PerTeamSharedSecretBoxes{
248		Generation:       generation,
249		EncryptingKid:    senderNaclDHKey.GetKID(),
250		Nonce:            nonce.PrefixEncoded(),
251		Boxes:            boxes,
252		BoxSummaryHash:   boxPublicSummary.EncodeToString(),
253		boxPublicSummary: boxPublicSummary,
254	}, nil
255}
256
257func (t *TeamKeyManager) recipientBoxes(secret keybase1.PerTeamKeySeed, nonce *nonce24, senderKey libkb.NaclDHKeyPair, recipients map[keybase1.UserVersion]keybase1.PerUserKey) (map[keybase1.UID]string, error) {
258	boxes := make(map[keybase1.UID]string)
259	for uv, recipientPerUserKey := range recipients {
260		boxStruct, err := t.recipientBox(secret, nonce, senderKey, recipientPerUserKey)
261		if err != nil {
262			return nil, err
263		}
264
265		encodedArray, err := msgpack.Encode(boxStruct)
266		if err != nil {
267			return nil, err
268		}
269
270		boxes[uv.Uid] = base64.StdEncoding.EncodeToString(encodedArray)
271	}
272
273	return boxes, nil
274}
275
276func (t *TeamKeyManager) recipientBox(secret keybase1.PerTeamKeySeed, nonce *nonce24, senderKey libkb.NaclDHKeyPair, recipient keybase1.PerUserKey) (*PerTeamSharedSecretBox, error) {
277	recipientPerUserGenericKeypair, err := libkb.ImportKeypairFromKID(recipient.EncKID)
278	if err != nil {
279		return nil, err
280	}
281	recipientPerUserNaclKeypair, ok := recipientPerUserGenericKeypair.(libkb.NaclDHKeyPair)
282	if !ok {
283		return nil, fmt.Errorf("got an unexpected key type for recipient KID in sharedTeamKeyBox: %T", recipientPerUserGenericKeypair)
284	}
285
286	nonceBytes, nonceCounter := nonce.Nonce()
287	ctext := box.Seal(nil, secret[:], &nonceBytes, ((*[32]byte)(&recipientPerUserNaclKeypair.Public)), ((*[32]byte)(senderKey.Private)))
288
289	boxStruct := PerTeamSharedSecretBox{
290		Version:         libkb.SharedTeamKeyBoxVersion1,
291		PerUserKeySeqno: recipient.Seqno,
292		NonceCounter:    nonceCounter,
293		Ctext:           ctext,
294	}
295
296	return &boxStruct, nil
297}
298
299func (t *TeamKeyManager) perTeamKeySection() (*SCPerTeamKey, error) {
300	sigKey, err := t.SigningKey()
301	if err != nil {
302		return nil, err
303	}
304	encKey, err := t.EncryptionKey()
305	if err != nil {
306		return nil, err
307	}
308	return &SCPerTeamKey{
309		Generation: t.generation,
310		SigKID:     sigKey.GetKID(),
311		EncKID:     encKey.GetKID(),
312	}, nil
313}
314
315func (t *TeamKeyManager) setNextSharedSecret(mctx libkb.MetaContext, secret keybase1.PerTeamKeySeed) (err error) {
316
317	check, err := computeSeedCheck(t.id, secret, &t.check)
318	if err != nil {
319		return err
320	}
321
322	t.sharedSecret = secret
323	t.check = *check
324
325	// bump generation number
326	t.generation++
327
328	// clear out derived keys
329	t.signingKey = nil
330	t.encryptionKey = nil
331
332	mctx.Debug("TeamKeyManager: set next shared secret, generation %d", t.generation)
333
334	return nil
335}
336
337type prevKeySealedDecoded struct {
338	_struct bool `codec:",toarray"` //nolint
339	Version int
340	Nonce   [24]byte
341	Key     []byte
342}
343
344type prevKeySealedEncoded string
345
346func encodeSealedPrevKey(nonceBytes [24]byte, key []byte) (prevKeySealedEncoded, error) {
347	prevKey := prevKeySealedDecoded{
348		Version: 1,
349		Nonce:   nonceBytes,
350		Key:     key,
351	}
352	packed, err := msgpack.Encode(prevKey)
353	if err != nil {
354		return "", err
355	}
356	encoded := base64.StdEncoding.EncodeToString(packed)
357	return prevKeySealedEncoded(encoded), nil
358}
359
360func decodeSealedPrevKey(e prevKeySealedEncoded) (nonce [24]byte, ctext []byte, err error) {
361	decoded, err := base64.StdEncoding.DecodeString(string(e))
362	if err != nil {
363		return nonce, nil, err
364	}
365	var tmp prevKeySealedDecoded
366	err = msgpack.Decode(&tmp, decoded)
367	if err != nil {
368		return nonce, nil, err
369	}
370	if tmp.Version != 1 {
371		return nonce, nil, fmt.Errorf("can only handle V1 encrypted prevs")
372	}
373	return tmp.Nonce, tmp.Key, nil
374}
375
376func newSharedSecret() (ret keybase1.PerTeamKeySeed, err error) {
377	n, err := rand.Read(ret[:])
378	if err != nil {
379		return ret, err
380	}
381	if n != len(ret) {
382		return ret, errors.New("short random read in newSharedSecret")
383	}
384	return ret, nil
385}
386
387func derivedSecret(secret keybase1.PerTeamKeySeed, context string) []byte {
388	if secret.IsZero() {
389		panic("Should never be using a zero key in derivedSecret; something went terribly wrong")
390	}
391	digest := hmac.New(sha512.New, secret[:])
392	_, _ = digest.Write([]byte(context))
393	return digest.Sum(nil)[:32]
394}
395
396// Decrypt a single prev secretbox.
397// Takes a prev to decrypt and the seed of the successor generation.
398// For example (prev[3], seed[4]) -> seed[3]
399func decryptPrevSingle(ctx context.Context,
400	prevToDecrypt prevKeySealedEncoded, successor keybase1.PerTeamKeySeed) (*keybase1.PerTeamKeySeed, error) {
401	if successor.IsZero() {
402		return nil, fmt.Errorf("Got 0 key, which can't be right")
403	}
404	if len(prevToDecrypt) == 0 {
405		return nil, fmt.Errorf("zero-length encoded prev")
406	}
407	nonce, ctext, err := decodeSealedPrevKey(prevToDecrypt)
408	if err != nil {
409		return nil, err
410	}
411	var keyFixed [32]byte
412	// prev key to decrypt with
413	key := derivedSecret(successor, libkb.TeamPrevKeySecretBoxDerivationString)
414	copy(keyFixed[:], key)
415	opened, ok := secretbox.Open(nil, ctext, &nonce, &keyFixed)
416	if !ok {
417		return nil, fmt.Errorf("prev decryption failed")
418	}
419	ret, err := keybase1.PerTeamKeySeedFromBytes(opened)
420	return &ret, err
421}
422
423func computeSeedCheck(id keybase1.TeamID, seed keybase1.PerTeamKeySeed, prev *keybase1.PerTeamSeedCheck) (*keybase1.PerTeamSeedCheck, error) {
424
425	var prevValue keybase1.PerTeamSeedCheckValue
426	switch {
427	case prev == nil:
428		tmp := []byte(libkb.TeamKeySeedCheckDerivationString)
429		tmp = append(tmp, byte(0))
430		tmp = append(tmp, id.ToBytes()...)
431		prevValue = keybase1.PerTeamSeedCheckValue(tmp)
432	case prev.Version != keybase1.PerTeamSeedCheckVersion_V1:
433		return nil, fmt.Errorf("cannot handle PerTeamSeedCheck version > 1")
434	case prev.Version == keybase1.PerTeamSeedCheckVersion_V1:
435		prevValue = prev.Value
436	}
437
438	g := func(seed keybase1.PerTeamKeySeed, prev keybase1.PerTeamSeedCheckValue) keybase1.PerTeamSeedCheckValue {
439		digest := hmac.New(sha512.New, seed[:])
440		_, _ = digest.Write([]byte(prev))
441		sum := digest.Sum(nil)[:32]
442		return keybase1.PerTeamSeedCheckValue(sum)
443	}
444
445	return &keybase1.PerTeamSeedCheck{
446		Version: keybase1.PerTeamSeedCheckVersion_V1,
447		Value:   g(seed, prevValue),
448	}, nil
449}
450
451// computeSeedChecks looks at a sequence of checks, newest to oldest, trying to find the first nil. Once it finds
452// such a nil, it goes forward and fills in the checks, based on the current seeds. There's an unfortunately level
453// of indirection since we need the same code to work over slow and fast team loading; thus we have setters and getters
454// as functions. But the idea is the same in both cases. Not that we're assuming there aren't "holes". That the nils
455// are at the back of the sequence and not interspersed throughout. This is valid so long as we always compute
456// these checks on load of new links.
457func computeSeedChecks(ctx context.Context, teamID keybase1.TeamID, latestChainGen keybase1.PerTeamKeyGeneration, getter func(g keybase1.PerTeamKeyGeneration) (*keybase1.PerTeamSeedCheck, keybase1.PerTeamKeySeed, error), setter func(g keybase1.PerTeamKeyGeneration, c keybase1.PerTeamSeedCheck)) error {
458
459	var firstNonNilCheck keybase1.PerTeamKeyGeneration
460	var foundLinkToUpdate bool
461
462	for i := latestChainGen; i >= 1; i-- {
463		check, _, err := getter(i)
464		if err != nil {
465			return err
466		}
467		if check != nil {
468			firstNonNilCheck = i
469			break
470		}
471		foundLinkToUpdate = true
472	}
473
474	if !foundLinkToUpdate {
475		// NoOp, we're all up-to-date
476		return nil
477	}
478
479	var prev *keybase1.PerTeamSeedCheck
480
481	if firstNonNilCheck > keybase1.PerTeamKeyGeneration(0) {
482		var err error
483		prev, _, err = getter(firstNonNilCheck)
484		if err != nil {
485			return err
486		}
487		if prev == nil {
488			return fmt.Errorf("unexpected nil PerTeamKeySeedsUnverified.Check at %d", firstNonNilCheck)
489		}
490	}
491
492	start := firstNonNilCheck + keybase1.PerTeamKeyGeneration(1)
493
494	for i := start; i <= latestChainGen; i++ {
495		_, seed, err := getter(i)
496		if err != nil {
497			return err
498		}
499		check, err := computeSeedCheck(teamID, seed, prev)
500		if err != nil {
501			return err
502		}
503		setter(i, *check)
504		prev = check
505	}
506	return nil
507}
508