1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"fmt"
8
9	"github.com/keybase/client/go/libkb"
10
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12)
13
14// SaltpackUserKeyfinder is an engine to find Per User Keys (PUK). Users can also be loaded by assertions, possibly tracking them if necessary.
15// This engine does not find per team keys, which capability is implemented by SaltpackRecipientKeyfinder in the saltpackKeyHelpers package.
16type SaltpackUserKeyfinder struct {
17	Arg                           libkb.SaltpackRecipientKeyfinderArg
18	RecipientEntityKeyMap         map[keybase1.UserOrTeamID]([]keybase1.KID)
19	RecipientDeviceAndPaperKeyMap map[keybase1.UID]([]keybase1.KID)
20	UsingSBS                      bool
21	SBSAssertion                  string
22}
23
24var _ libkb.Engine2 = (*SaltpackUserKeyfinder)(nil)
25var _ libkb.SaltpackRecipientKeyfinderEngineInterface = (*SaltpackUserKeyfinder)(nil)
26
27// NewSaltpackUserKeyfinderAsInterface creates a SaltpackUserKeyfinder engine.
28func NewSaltpackUserKeyfinderAsInterface(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface {
29	return NewSaltpackUserKeyfinder(arg)
30}
31
32func NewSaltpackUserKeyfinder(arg libkb.SaltpackRecipientKeyfinderArg) *SaltpackUserKeyfinder {
33	return &SaltpackUserKeyfinder{
34		Arg:                           arg,
35		RecipientEntityKeyMap:         make(map[keybase1.UserOrTeamID]([]keybase1.KID)),
36		RecipientDeviceAndPaperKeyMap: make(map[keybase1.UID]([]keybase1.KID)),
37	}
38}
39
40// Name is the unique engine name.
41func (e *SaltpackUserKeyfinder) Name() string {
42	return "SaltpackUserKeyfinder"
43}
44
45// Prereqs returns the engine prereqs.
46func (e *SaltpackUserKeyfinder) Prereqs() Prereqs {
47	return Prereqs{}
48}
49
50// RequiredUIs returns the required UIs.
51func (e *SaltpackUserKeyfinder) RequiredUIs() []libkb.UIKind {
52	return []libkb.UIKind{}
53}
54
55// SubConsumers returns the other UI consumers for this engine.
56func (e *SaltpackUserKeyfinder) SubConsumers() []libkb.UIConsumer {
57	return []libkb.UIConsumer{}
58}
59
60func (e *SaltpackUserKeyfinder) GetPublicKIDs() []keybase1.KID {
61	var r []keybase1.KID
62	for _, keys := range e.RecipientDeviceAndPaperKeyMap {
63		r = append(r, keys...)
64	}
65	for _, keys := range e.RecipientEntityKeyMap {
66		r = append(r, keys...)
67	}
68
69	return r
70}
71
72func (e *SaltpackUserKeyfinder) GetSymmetricKeys() []libkb.SaltpackReceiverSymmetricKey {
73	return []libkb.SaltpackReceiverSymmetricKey{}
74}
75
76func (e *SaltpackUserKeyfinder) UsedUnresolvedSBSAssertion() (bool, string) {
77	return e.UsingSBS, e.SBSAssertion
78}
79
80func (e *SaltpackUserKeyfinder) Run(m libkb.MetaContext) (err error) {
81	defer m.Trace("SaltpackUserKeyfinder#Run", &err)()
82
83	if len(e.Arg.TeamRecipients) != 0 {
84		m.Debug("tried to use SaltpackUserKeyfinder for a team. This should never happen")
85		return fmt.Errorf("cannot find keys for teams")
86	}
87
88	err = e.AddOwnKeysIfNeeded(m)
89	if err != nil {
90		return err
91	}
92
93	err = e.identifyAndAddRecipients(m)
94	if err != nil {
95		return err
96	}
97	return nil
98}
99
100func (e *SaltpackUserKeyfinder) AddOwnKeysIfNeeded(m libkb.MetaContext) error {
101	if e.Arg.NoSelfEncrypt {
102		return nil
103	}
104	if !m.ActiveDevice().Valid() {
105		return libkb.NewLoginRequiredError("need to be logged in or use --no-self-encrypt")
106	}
107	arg := libkb.NewLoadUserArgWithMetaContext(m).WithUID(m.ActiveDevice().UID()).WithForcePoll(!e.Arg.NoForcePoll)
108	upak, _, err := m.G().GetUPAKLoader().LoadV2(arg)
109	if err != nil {
110		return err
111	}
112	return e.AddUserRecipient(m, &upak.Current)
113}
114
115// identifyAndAddRecipients adds the KID corresponding to each recipient to the recipientMap
116func (e *SaltpackUserKeyfinder) identifyAndAddRecipients(m libkb.MetaContext) error {
117	for _, u := range e.Arg.Recipients {
118		// TODO make these lookups in parallel (maybe using sync.WaitGroup)
119		upk, err := e.IdentifyUser(m, u) // For existing users
120		switch {
121		case err == nil:
122			// nothing to do here
123		case libkb.IsIdentifyProofError(err):
124			return fmt.Errorf("Cannot encrypt for %v as their account has changed since you last followed them (it might have been compromised!): please review their identity (with `keybase follow %v`) and then try again (err = %v)", u, u, err)
125		case libkb.IsNotFoundError(err) || libkb.IsResolutionError(err):
126			return fmt.Errorf("Cannot find keys for %v: it is not an assertion for a registered user (err = %v)", u, err)
127		default:
128			return fmt.Errorf("Error while adding keys for %v: %v", u, err)
129		}
130		err = e.AddUserRecipient(m, upk)
131		if err != nil {
132			return err
133		}
134	}
135	return nil
136}
137
138func (e *SaltpackUserKeyfinder) IdentifyUser(m libkb.MetaContext, user string) (upk *keybase1.UserPlusKeysV2, err error) {
139
140	Arg := keybase1.Identify2Arg{
141		UserAssertion: user,
142		Reason: keybase1.IdentifyReason{
143			Type: keybase1.IdentifyReasonType_ENCRYPT,
144		},
145		AlwaysBlock:      true,
146		IdentifyBehavior: keybase1.TLFIdentifyBehavior_SALTPACK,
147	}
148	eng := NewResolveThenIdentify2(m.G(), &Arg)
149	if err := RunEngine2(m, eng); err != nil {
150		return nil, err
151	}
152
153	engRes, err := eng.Result(m)
154	if err != nil {
155		return nil, err
156	}
157
158	return &engRes.Upk.Current, nil
159}
160
161func (e *SaltpackUserKeyfinder) hasRecipientDeviceOrPaperKeys(id keybase1.UID) bool {
162	_, ok := e.RecipientDeviceAndPaperKeyMap[id]
163	return ok
164}
165
166func (e *SaltpackUserKeyfinder) hasRecipientEntityKeys(id keybase1.UserOrTeamID) bool {
167	_, ok := e.RecipientEntityKeyMap[id]
168	return ok
169}
170
171func (e *SaltpackUserKeyfinder) AddUserRecipient(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error {
172	err := e.AddDeviceAndPaperKeys(m, upk)
173	err2 := e.AddPUK(m, upk)
174
175	// If we managed to add at least one key for upk, we are happy.
176	if (!(e.Arg.UseDeviceKeys || e.Arg.UsePaperKeys) || err != nil) && (!e.Arg.UseEntityKeys || err2 != nil) {
177		return libkb.PickFirstError(err, err2)
178	}
179	return nil
180}
181
182func (e *SaltpackUserKeyfinder) isPaperEncryptionKey(key *keybase1.PublicKeyV2NaCl, deviceKeys *(map[keybase1.KID]keybase1.PublicKeyV2NaCl)) bool {
183	return libkb.KIDIsDeviceEncrypt(key.Base.Kid) && key.Parent != nil && (*deviceKeys)[*key.Parent].DeviceType == keybase1.DeviceTypeV2_PAPER
184}
185
186// AddPUK returns no error if it adds at least one key (or no paper keys and device keys were requested), otherwise it returns a libkb.NoNaClEncryptionKeyError
187func (e *SaltpackUserKeyfinder) AddDeviceAndPaperKeys(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error {
188	if !e.Arg.UsePaperKeys && !e.Arg.UseDeviceKeys {
189		// No need to add anything
190		return nil
191	}
192
193	if e.hasRecipientDeviceOrPaperKeys(upk.Uid) {
194		// This user's keys were already added
195		return nil
196	}
197
198	var keys []keybase1.KID
199
200	hasPaperKey := false
201	hasDeviceKey := false
202	hasPUK := len(upk.PerUserKeys) > 0
203
204	for KID, key := range upk.DeviceKeys {
205		// Note: for Nacl encryption keys, the DeviceType field is not set, so we need to look at the "parent" signing key
206		if e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
207			hasPaperKey = true
208			if e.Arg.UsePaperKeys {
209				keys = append(keys, KID)
210				m.Debug("adding user %v's paper key", upk.Username)
211			}
212		}
213
214		if libkb.KIDIsDeviceEncrypt(KID) && !e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
215			hasDeviceKey = true
216			if e.Arg.UseDeviceKeys {
217				keys = append(keys, KID)
218				m.Debug("adding user %v's device key", upk.Username)
219			}
220		}
221	}
222
223	e.RecipientDeviceAndPaperKeyMap[upk.Uid] = keys
224
225	if len(keys) == 0 {
226		m.Debug("did not add any device or paper keys for %v", upk.Username)
227		return libkb.NoNaClEncryptionKeyError{
228			Username:     upk.Username,
229			HasPGPKey:    len(upk.PGPKeys) > 0,
230			HasPUK:       hasPUK,
231			HasDeviceKey: hasDeviceKey,
232			HasPaperKey:  hasPaperKey,
233		}
234	}
235
236	return nil
237}
238
239// AddPUK returns no error unless the user has no PUK, in which case it returns a libkb.NoNaClEncryptionKeyError
240func (e *SaltpackUserKeyfinder) AddPUK(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error {
241	if !e.Arg.UseEntityKeys {
242		// No need to add anything
243		return nil
244	}
245
246	if e.hasRecipientEntityKeys(upk.Uid.AsUserOrTeam()) {
247		// This user's keys were already added
248		return nil
249	}
250
251	hasPUK := len(upk.PerUserKeys) > 0
252
253	if !hasPUK {
254		hasPaperKey := false
255		hasDeviceKey := false
256		for KID, key := range upk.DeviceKeys {
257			if e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
258				hasPaperKey = true
259			}
260			if libkb.KIDIsDeviceEncrypt(KID) && !e.isPaperEncryptionKey(&key, &upk.DeviceKeys) {
261				hasDeviceKey = true
262			}
263		}
264
265		m.Debug("did not add any per user keys for %v", upk.Username)
266		return libkb.NoNaClEncryptionKeyError{
267			Username:     upk.Username,
268			HasPGPKey:    len(upk.PGPKeys) > 0,
269			HasPUK:       hasPUK,
270			HasDeviceKey: hasDeviceKey,
271			HasPaperKey:  hasPaperKey,
272		}
273	}
274
275	// We ensured above that the user has a PUK, so the loop below will be executed at least once
276	maxGen := -1
277	var lk keybase1.KID
278	for _, k := range upk.PerUserKeys {
279		if k.Gen > maxGen {
280			maxGen = k.Gen
281			lk = k.EncKID
282		}
283	}
284	if lk == "" {
285		panic("This should never happen, user has a PUK with a nil KID")
286	}
287
288	m.Debug("adding user %v's latest per user key", upk.Username)
289	e.RecipientEntityKeyMap[upk.Uid.AsUserOrTeam()] = []keybase1.KID{lk}
290
291	return nil
292}
293