1// Copyright 2016 Keybase Inc. All rights reserved.
2// Use of this source code is governed by a BSD
3// license that can be found in the LICENSE file.
4
5package libkbfs
6
7import (
8	"github.com/keybase/client/go/kbfs/kbfscodec"
9	"github.com/keybase/client/go/kbfs/kbfscrypto"
10	"github.com/keybase/client/go/libkb"
11	"github.com/keybase/client/go/protocol/keybase1"
12	"github.com/pkg/errors"
13	"golang.org/x/net/context"
14)
15
16type perTeamKeyPair struct {
17	privKey kbfscrypto.TLFPrivateKey
18	pubKey  kbfscrypto.TLFPublicKey
19}
20
21type perTeamKeyPairs map[keybase1.PerTeamKeyGeneration]perTeamKeyPair
22
23// CryptoLocal implements the Crypto interface by using a local
24// signing key and a local crypt private key.
25type CryptoLocal struct {
26	CryptoCommon
27	kbfscrypto.SigningKeySigner
28	cryptPrivateKey kbfscrypto.CryptPrivateKey
29	teamPrivateKeys map[keybase1.TeamID]perTeamKeyPairs
30}
31
32var _ Crypto = (*CryptoLocal)(nil)
33
34// NewCryptoLocal constructs a new CryptoLocal instance with the given
35// signing key.
36func NewCryptoLocal(codec kbfscodec.Codec,
37	signingKey kbfscrypto.SigningKey,
38	cryptPrivateKey kbfscrypto.CryptPrivateKey,
39	blockCryptVersioner blockCryptVersioner) *CryptoLocal {
40	return &CryptoLocal{
41		MakeCryptoCommon(codec, blockCryptVersioner),
42		kbfscrypto.SigningKeySigner{Key: signingKey},
43		cryptPrivateKey,
44		make(map[keybase1.TeamID]perTeamKeyPairs),
45	}
46}
47
48// DecryptTLFCryptKeyClientHalf implements the Crypto interface for
49// CryptoLocal.
50func (c *CryptoLocal) DecryptTLFCryptKeyClientHalf(ctx context.Context,
51	publicKey kbfscrypto.TLFEphemeralPublicKey,
52	encryptedClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf) (
53	kbfscrypto.TLFCryptKeyClientHalf, error) {
54	return kbfscrypto.DecryptTLFCryptKeyClientHalf(
55		c.cryptPrivateKey, publicKey, encryptedClientHalf)
56}
57
58// DecryptTLFCryptKeyClientHalfAny implements the Crypto interface for
59// CryptoLocal.
60func (c *CryptoLocal) DecryptTLFCryptKeyClientHalfAny(ctx context.Context,
61	keys []EncryptedTLFCryptKeyClientAndEphemeral, _ bool) (
62	clientHalf kbfscrypto.TLFCryptKeyClientHalf, index int, err error) {
63	if len(keys) == 0 {
64		return kbfscrypto.TLFCryptKeyClientHalf{}, -1,
65			errors.WithStack(NoKeysError{})
66	}
67	var firstNonDecryptionErr error
68	for i, k := range keys {
69		clientHalf, err := c.DecryptTLFCryptKeyClientHalf(
70			ctx, k.EPubKey, k.ClientHalf)
71		if err != nil {
72			_, isDecryptionError :=
73				errors.Cause(err).(libkb.DecryptionError)
74			if firstNonDecryptionErr == nil && !isDecryptionError {
75				firstNonDecryptionErr = err
76			}
77			continue
78		}
79		return clientHalf, i, nil
80	}
81	// This is to mimic the behavior in
82	// CryptoClient.DecryptTLFCryptKeyClientHalfAny, which is to,
83	// if all calls to prepareTLFCryptKeyClientHalf failed, return
84	// the first prep error, and otherwise to return the error
85	// from the service, which is usually libkb.DecryptionError.
86	if firstNonDecryptionErr != nil {
87		return kbfscrypto.TLFCryptKeyClientHalf{}, -1,
88			firstNonDecryptionErr
89	}
90	return kbfscrypto.TLFCryptKeyClientHalf{}, -1,
91		errors.WithStack(libkb.DecryptionError{})
92}
93
94func (c *CryptoLocal) pubKeyForTeamKeyGeneration(
95	teamID keybase1.TeamID, keyGen keybase1.PerTeamKeyGeneration) (
96	pubKey kbfscrypto.TLFPublicKey, err error) {
97	if c.teamPrivateKeys[teamID] == nil {
98		c.teamPrivateKeys[teamID] = make(perTeamKeyPairs)
99	}
100
101	teamKeys := c.teamPrivateKeys[teamID]
102	kp, ok := teamKeys[keyGen]
103	// If a key pair doesn't exist yet for this keygen, generate a
104	// random one.
105	if !ok {
106		pubKey, privKey, _, err := c.MakeRandomTLFKeys()
107		if err != nil {
108			return kbfscrypto.TLFPublicKey{}, err
109		}
110		kp = perTeamKeyPair{privKey, pubKey}
111		c.teamPrivateKeys[teamID][keyGen] = kp
112	}
113
114	return kp.pubKey, nil
115}
116
117// DecryptTeamMerkleLeaf implements the Crypto interface for
118// CryptoLocal.
119func (c *CryptoLocal) DecryptTeamMerkleLeaf(
120	ctx context.Context, teamID keybase1.TeamID,
121	publicKey kbfscrypto.TLFEphemeralPublicKey,
122	encryptedMerkleLeaf kbfscrypto.EncryptedMerkleLeaf,
123	minKeyGen keybase1.PerTeamKeyGeneration) (decryptedData []byte, err error) {
124	perTeamKeys := c.teamPrivateKeys[teamID]
125	maxKeyGen := keybase1.PerTeamKeyGeneration(len(perTeamKeys))
126	for i := minKeyGen; i <= maxKeyGen; i++ {
127		decryptedData, err := kbfscrypto.DecryptMerkleLeaf(
128			perTeamKeys[i].privKey, publicKey, encryptedMerkleLeaf)
129		if err == nil {
130			return decryptedData, nil
131		}
132	}
133
134	return nil, errors.WithStack(libkb.DecryptionError{})
135}
136
137// Shutdown implements the Crypto interface for CryptoLocal.
138func (c *CryptoLocal) Shutdown() {}
139