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	"errors"
8	"fmt"
9
10	"github.com/keybase/client/go/libkb"
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12)
13
14// PassphraseChange engine is used for changing the user's passphrase, either
15// by replacement or by force.
16type PassphraseChange struct {
17	arg        *keybase1.PassphraseChangeArg
18	me         *libkb.User
19	usingPaper bool
20	libkb.Contextified
21}
22
23// NewPassphraseChange creates a new engine for changing user passphrases,
24// either if the current passphrase is known, or in "force" mode
25func NewPassphraseChange(g *libkb.GlobalContext, a *keybase1.PassphraseChangeArg) *PassphraseChange {
26	return &PassphraseChange{
27		arg:          a,
28		Contextified: libkb.NewContextified(g),
29	}
30}
31
32// Name provides the name of the engine for the engine interface
33func (c *PassphraseChange) Name() string {
34	return "PassphraseChange"
35}
36
37// Prereqs returns engine prereqs
38func (c *PassphraseChange) Prereqs() Prereqs {
39	if c.arg.Force {
40		return Prereqs{}
41	}
42
43	return Prereqs{Device: true}
44}
45
46// RequiredUIs returns the required UIs.
47func (c *PassphraseChange) RequiredUIs() []libkb.UIKind {
48	return []libkb.UIKind{
49		libkb.SecretUIKind,
50	}
51}
52
53// SubConsumers requires the other UI consumers of this engine
54func (c *PassphraseChange) SubConsumers() []libkb.UIConsumer {
55	return []libkb.UIConsumer{
56		&PaperKeyGen{},
57	}
58}
59
60// Run the engine
61func (c *PassphraseChange) Run(m libkb.MetaContext) (err error) {
62
63	m = m.WithLogTag("PPCHNG")
64
65	defer m.Trace("PassphraseChange#Run", &err)()
66	defer func() {
67		m.G().SKBKeyringMu.Unlock()
68	}()
69	m.G().SKBKeyringMu.Lock()
70	m.Debug("| Acquired SKBKeyringMu mutex")
71
72	if len(c.arg.Passphrase) < libkb.MinPassphraseLength {
73		return libkb.PassphraseError{Msg: "too short"}
74	}
75
76	if err = c.loadMe(); err != nil {
77		return
78	}
79
80	if _, w := m.ActiveDevice().SyncSecrets(m); w != nil {
81		m.Debug("| failed to run secret syncer: %s", w)
82	}
83
84	m = m.WithNewProvisionalLoginContextForUser(c.me)
85
86	if c.arg.Force {
87		err = c.runForcedUpdate(m)
88	} else {
89		err = c.runStandardUpdate(m)
90	}
91	if err != nil {
92		return err
93	}
94
95	// If the passphrase changes, it's known. Do this in case we aren't getting
96	// gregors for some reason (standalone or test) - it will at least update
97	// this device.
98	libkb.MaybeSavePassphraseState(m, keybase1.PassphraseState_KNOWN)
99
100	// We used to sync secrets here, but sync secrets in runForceUpdate
101	// or runStandardUpdate, since the temporary login information won't
102	// persist past the scope of these functions.
103	return nil
104}
105
106// findPaperKeys checks if the user has paper keys.  If he/she
107// does, it prompts for a paper key phrase.  This is used to
108// regenerate paper keys, which are then matched against the
109// paper keys found in the keyfamily.
110func (c *PassphraseChange) findPaperKeys(m libkb.MetaContext) (*libkb.DeviceWithKeys, error) {
111	kp, err := findPaperKeys(m, c.me)
112	if err != nil {
113		m.Debug("findPaperKeys error: %s", err)
114		return nil, err
115	}
116	m.Debug("findPaperKeys success")
117	c.usingPaper = true
118	return kp, nil
119}
120
121// findUpdateKeys looks for keys to perform the passphrase update.
122// The first choice is device keys.  If that fails, it will look
123// for backup keys.  If backup keys are necessary, then it will
124// also log the user in with the backup keys.
125func (c *PassphraseChange) findUpdateDevice(m libkb.MetaContext) (ad *libkb.ActiveDevice, err error) {
126	defer m.Trace("PassphraseChange#findUpdateDevice", &err)()
127	if ad = m.G().ActiveDevice; ad.Valid() {
128		m.Debug("| returning globally active device key")
129		return ad, nil
130	}
131	kp, err := c.findPaperKeys(m)
132	if err != nil {
133		m.Debug("| error fetching paper keys")
134		return nil, err
135	}
136	ad = libkb.NewActiveDeviceWithDeviceWithKeys(m, m.CurrentUserVersion(), kp)
137	m.Debug("| installing paper key as thread-local active device")
138	return ad, nil
139}
140
141func (c *PassphraseChange) forceUpdatePassphrase(m libkb.MetaContext, sigKey libkb.GenericKey, ppGen libkb.PassphraseGeneration, oldClientHalf libkb.LKSecClientHalf) (err error) {
142
143	defer m.Trace("PassphraseChange#forceUpdatePassphrase", &err)()
144
145	// Don't update server-synced pgp keys when recovering.
146	// This will render any server-synced pgp keys unrecoverable from the server.
147	// TODO would it responsible to ask the server to delete them?
148	pgpKeys, nPgpKeysLost, err := c.findAndDecryptPrivatePGPKeysLossy(m)
149	if err != nil {
150		return err
151	}
152
153	if nPgpKeysLost > 0 {
154		m.Debug("PassphraseChange.runForcedUpdate: Losing %v synced keys", nPgpKeysLost)
155	}
156
157	// Ready the update argument; almost done, but we need some more stuff.
158	payload, err := c.commonArgs(m, oldClientHalf, pgpKeys, ppGen)
159	if err != nil {
160		return err
161	}
162
163	// get the new passphrase hash out of the args
164	pwh, ok := payload["pwh"].(string)
165	if !ok || len(pwh) == 0 {
166		return errors.New("no pwh found in common args")
167	}
168
169	// get the new PDPKA5 KID out of the args
170	pdpka5kid, ok := payload["pdpka5_kid"].(string)
171	if !ok || len(pdpka5kid) == 0 {
172		return errors.New("no pdpka5kid found in common args")
173	}
174
175	// Generate a signature with our unlocked sibling key from device.
176	proof, err := c.me.UpdatePassphraseProof(m, sigKey, pwh, ppGen+1, pdpka5kid)
177	if err != nil {
178		return err
179	}
180
181	sig, _, _, err := libkb.SignJSON(proof, sigKey)
182	if err != nil {
183		return err
184	}
185	payload["sig"] = sig
186	payload["signing_kid"] = sigKey.GetKID()
187
188	postArg := libkb.APIArg{
189		Endpoint:    "passphrase/sign",
190		SessionType: libkb.APISessionTypeREQUIRED,
191		JSONPayload: payload,
192	}
193
194	// Important to pass a MetaContext here to pick up the provisional login context
195	// or an ActiveDevice that is thread-local.
196	_, err = m.G().API.PostJSON(m, postArg)
197	if err != nil {
198		return fmt.Errorf("api post to passphrase/sign error: %s", err)
199	}
200	return nil
201}
202
203// 1. Get keys for decryption and signing
204// 2. If necessary, log in with backup keys
205// 3. Get lks client half from server
206// 4. Post an update passphrase proof
207func (c *PassphraseChange) runForcedUpdate(m libkb.MetaContext) (err error) {
208	defer m.Trace("PassphraseChange#runForcedUpdate", &err)()
209
210	ad, err := c.findUpdateDevice(m)
211	if err != nil {
212		return
213	}
214	if ad == nil {
215		return libkb.NoSecretKeyError{}
216	}
217
218	enc, err := ad.EncryptionKey()
219	if err != nil {
220		return err
221	}
222	sig, err := ad.SigningKey()
223	if err != nil {
224		return err
225	}
226
227	m = m.WithActiveDevice(ad)
228	ppGen, oldClientHalf, err := fetchLKS(m, enc)
229	if err != nil {
230		return
231	}
232
233	err = c.forceUpdatePassphrase(m, sig, ppGen, oldClientHalf)
234	if err != nil {
235		return err
236	}
237
238	_, err = m.SyncSecrets()
239	if err != nil {
240		return err
241	}
242
243	m = m.WithGlobalActiveDevice()
244	// Reset passphrase stream cache so that subsequent updates go through
245	// without a problem (see CORE-3933)
246	m.ActiveDevice().ClearCaches()
247
248	return nil
249}
250
251// runStandardUpdate is for when the user knows the current password.
252func (c *PassphraseChange) runStandardUpdate(m libkb.MetaContext) (err error) {
253
254	defer m.Trace("PassphraseChange.runStandardUpdate", &err)()
255
256	var ppStream *libkb.PassphraseStream
257	if len(c.arg.OldPassphrase) == 0 {
258		ppStream, err = libkb.GetPassphraseStreamViaPromptInLoginContext(m)
259	} else {
260		ppStream, err = libkb.VerifyPassphraseGetStreamInLoginContext(m, c.arg.OldPassphrase)
261	}
262	if err != nil {
263		return err
264	}
265
266	pgpKeys, err := c.findAndDecryptPrivatePGPKeys(m)
267	if err != nil {
268		return err
269	}
270
271	gen := m.PassphraseStream().Generation()
272	oldClientHalf := m.PassphraseStream().LksClientHalf()
273
274	payload, err := c.commonArgs(m, oldClientHalf, pgpKeys, gen)
275	if err != nil {
276		return err
277	}
278
279	lp, err := libkb.ComputeLoginPackage2(m, ppStream)
280	if err != nil {
281		return err
282	}
283
284	payload["ppgen"] = gen
285	payload["old_pdpka4"] = lp.PDPKA4()
286	payload["old_pdpka5"] = lp.PDPKA5()
287
288	postArg := libkb.APIArg{
289		Endpoint:    "passphrase/replace",
290		SessionType: libkb.APISessionTypeREQUIRED,
291		JSONPayload: payload,
292	}
293
294	_, err = c.G().API.PostJSON(m, postArg)
295	if err != nil {
296		return err
297	}
298
299	_, err = m.SyncSecrets()
300	if err != nil {
301		return err
302	}
303
304	// Reset the passphrase stream cache on the global Active Device, since if it exists,
305	// it was for a previous version of the passphrase.
306	m = m.WithGlobalActiveDevice()
307	m.ActiveDevice().ClearCaches()
308
309	return nil
310}
311
312func (c *PassphraseChange) commonArgs(m libkb.MetaContext, oldClientHalf libkb.LKSecClientHalf, pgpKeys []libkb.GenericKey, existingGen libkb.PassphraseGeneration) (libkb.JSONPayload, error) {
313
314	salt, err := c.me.GetSalt()
315	if err != nil {
316		return nil, err
317	}
318
319	tsec, newPPStream, err := libkb.StretchPassphrase(c.G(), c.arg.Passphrase, salt)
320	if err != nil {
321		return nil, err
322	}
323	newPWH := newPPStream.PWHash()
324	newClientHalf := newPPStream.LksClientHalf()
325	pdpka5kid, err := newPPStream.PDPKA5KID()
326	if err != nil {
327		return nil, err
328	}
329
330	mask := oldClientHalf.ComputeMask(newClientHalf)
331
332	lksch := make(map[keybase1.KID]string)
333	devices := c.me.GetComputedKeyFamily().GetAllDevices()
334	for _, dev := range devices {
335		if !dev.IsActive() {
336			continue
337		}
338		key, err := c.me.GetComputedKeyFamily().GetEncryptionSubkeyForDevice(dev.ID)
339		if err != nil {
340			return nil, err
341		}
342		ctext, err := key.EncryptToString(newClientHalf.Bytes(), nil)
343		if err != nil {
344			return nil, err
345		}
346		lksch[key.GetKID()] = ctext
347	}
348
349	payload := make(libkb.JSONPayload)
350	payload["pwh"] = libkb.HexArg(newPWH).String()
351	payload["pwh_version"] = libkb.ClientTriplesecVersion
352	payload["lks_mask"] = mask.EncodeToHex()
353	payload["lks_client_halves"] = lksch
354	payload["pdpka5_kid"] = pdpka5kid.String()
355
356	var encodedKeys []string
357	for _, key := range pgpKeys {
358		encoded, err := c.encodePrivatePGPKey(key, tsec, existingGen+1)
359		if err != nil {
360			return nil, err
361		}
362		encodedKeys = append(encodedKeys, encoded)
363	}
364	payload["private_keys"] = encodedKeys
365
366	return payload, nil
367}
368
369func (c *PassphraseChange) loadMe() (err error) {
370	c.me, err = libkb.LoadMe(libkb.NewLoadUserForceArg(c.G()))
371	return
372}
373
374// findAndDecryptPrivatePGPKeys gets the user's private pgp keys if any exist and decrypts them.
375func (c *PassphraseChange) findAndDecryptPrivatePGPKeys(m libkb.MetaContext) ([]libkb.GenericKey, error) {
376
377	var keyList []libkb.GenericKey
378
379	// Using a paper key makes TripleSec-synced keys unrecoverable
380	if c.usingPaper {
381		m.Debug("using a paper key, thus TripleSec-synced keys are unrecoverable")
382		return keyList, nil
383	}
384
385	// Only use the synced secret keys:
386	blocks, err := c.me.AllSyncedSecretKeys(m)
387	if err != nil {
388		return nil, err
389	}
390
391	secretRetriever := libkb.NewSecretStore(m, c.me.GetNormalizedName())
392
393	for _, block := range blocks {
394		parg := m.SecretKeyPromptArg(libkb.SecretKeyArg{}, "passphrase change")
395		key, err := block.PromptAndUnlock(m, parg, secretRetriever, c.me)
396		if err != nil {
397			return nil, err
398		}
399		keyList = append(keyList, key)
400	}
401
402	return keyList, nil
403}
404
405// findAndDecryptPrivatePGPKeysLossy gets the user's private pgp keys if any exist and attempts
406// to decrypt them without prompting the user. If any fail to decrypt, they are silently not returned.
407// The second return value is the number of keys which were not decrypted.
408func (c *PassphraseChange) findAndDecryptPrivatePGPKeysLossy(m libkb.MetaContext) ([]libkb.GenericKey, int, error) {
409
410	var keyList []libkb.GenericKey
411	nLost := 0
412
413	// Only use the synced secret keys:
414	blocks, err := c.me.AllSyncedSecretKeys(m)
415	if err != nil {
416		return nil, 0, err
417	}
418
419	secretRetriever := libkb.NewSecretStore(m, c.me.GetNormalizedName())
420
421	for _, block := range blocks {
422		key, err := block.UnlockNoPrompt(m, secretRetriever)
423		if err == nil {
424			keyList = append(keyList, key)
425		} else {
426			if err != libkb.ErrUnlockNotPossible {
427				return nil, 0, err
428			}
429			nLost++
430			m.Debug("findAndDecryptPrivatePGPKeysLossy: ignoring failure to decrypt key without prompt")
431		}
432	}
433
434	return keyList, nLost, nil
435}
436
437// encodePrivatePGPKey encrypts key with tsec and armor-encodes it.
438// It includes the passphrase generation in the data.
439func (c *PassphraseChange) encodePrivatePGPKey(key libkb.GenericKey, tsec libkb.Triplesec, gen libkb.PassphraseGeneration) (string, error) {
440	skb, err := libkb.ToServerSKB(c.G(), key, tsec, gen)
441	if err != nil {
442		return "", err
443	}
444
445	return skb.ArmoredEncode()
446}
447