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
14type RevokeMode int
15
16const (
17	RevokeKey RevokeMode = iota
18	RevokeDevice
19)
20
21type RevokeEngine struct {
22	libkb.Contextified
23	deviceID             keybase1.DeviceID
24	kid                  keybase1.KID
25	mode                 RevokeMode
26	forceSelf            bool
27	forceLast            bool
28	skipUserEKForTesting bool // Set for testing
29}
30
31type RevokeDeviceEngineArgs struct {
32	ID                   keybase1.DeviceID
33	ForceSelf            bool
34	ForceLast            bool
35	SkipUserEKForTesting bool
36}
37
38func NewRevokeDeviceEngine(g *libkb.GlobalContext, args RevokeDeviceEngineArgs) *RevokeEngine {
39	return &RevokeEngine{
40		deviceID:             args.ID,
41		mode:                 RevokeDevice,
42		forceSelf:            args.ForceSelf,
43		forceLast:            args.ForceLast,
44		skipUserEKForTesting: args.SkipUserEKForTesting,
45		Contextified:         libkb.NewContextified(g),
46	}
47}
48
49func NewRevokeKeyEngine(g *libkb.GlobalContext, kid keybase1.KID) *RevokeEngine {
50	return &RevokeEngine{
51		kid:          kid,
52		mode:         RevokeKey,
53		Contextified: libkb.NewContextified(g),
54	}
55}
56
57func (e *RevokeEngine) Name() string {
58	return "Revoke"
59}
60
61func (e *RevokeEngine) Prereqs() Prereqs {
62	return Prereqs{
63		Device: true,
64	}
65}
66
67func (e *RevokeEngine) RequiredUIs() []libkb.UIKind {
68	return []libkb.UIKind{
69		libkb.LogUIKind,
70		libkb.SecretUIKind,
71	}
72}
73
74func (e *RevokeEngine) SubConsumers() []libkb.UIConsumer {
75	return []libkb.UIConsumer{}
76}
77
78func (e *RevokeEngine) getKIDsToRevoke(me *libkb.User) ([]keybase1.KID, error) {
79	if e.mode == RevokeDevice {
80		deviceKeys, err := me.GetComputedKeyFamily().GetAllActiveKeysForDevice(e.deviceID)
81		if err != nil {
82			return nil, err
83		}
84		if len(deviceKeys) == 0 {
85			return nil, fmt.Errorf("No active keys to revoke for device %s.", e.deviceID)
86		}
87		return deviceKeys, nil
88	} else if e.mode == RevokeKey {
89		kid := e.kid
90		key, err := me.GetComputedKeyFamily().FindKeyWithKIDUnsafe(kid)
91		if err != nil {
92			return nil, err
93		}
94		if !libkb.IsPGP(key) {
95			return nil, fmt.Errorf("Key %s is not a PGP key. To revoke device keys, use the `device remove` command.", e.kid)
96		}
97		for _, activePGPKey := range me.GetComputedKeyFamily().GetActivePGPKeys(false /* sibkeys only */) {
98			if activePGPKey.GetKID().Equal(kid) {
99				return []keybase1.KID{kid}, nil
100			}
101		}
102		return nil, fmt.Errorf("PGP key %s is not active", e.kid)
103	} else {
104		return nil, fmt.Errorf("Unknown revoke mode: %d", e.mode)
105	}
106}
107
108func (e *RevokeEngine) explicitOrImplicitDeviceID(me *libkb.User) keybase1.DeviceID {
109	if e.mode == RevokeDevice {
110		return e.deviceID
111	}
112	for deviceID, device := range me.GetComputedKeyInfos().Devices {
113		if device.Kid.Equal(e.kid) {
114			return deviceID
115		}
116	}
117	// If we're revoking a PGP key, it won't match any device.
118	return ""
119}
120
121func (e *RevokeEngine) Run(mctx libkb.MetaContext) (err error) {
122	defer mctx.Trace(fmt.Sprintf("RevokeEngine (mode:%v)", e.mode), &err)()
123	return retryOnEphemeralRace(mctx, e.run)
124}
125
126func (e *RevokeEngine) run(m libkb.MetaContext) error {
127	e.G().LocalSigchainGuard().Set(m.Ctx(), "RevokeEngine")
128	defer e.G().LocalSigchainGuard().Clear(m.Ctx(), "RevokeEngine")
129
130	me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m))
131	if err != nil {
132		return err
133	}
134
135	currentDevice := m.G().Env.GetDeviceID()
136	var deviceID keybase1.DeviceID
137	if e.mode == RevokeDevice {
138		deviceID = e.deviceID
139		hasPGP := len(me.GetComputedKeyFamily().GetActivePGPKeys(false)) > 0
140
141		if len(me.GetComputedKeyFamily().GetAllActiveDevices()) == 1 {
142			passphraseState, err := libkb.LoadPassphraseState(m)
143			if err != nil {
144				return fmt.Errorf("could not load passphrase state: %s", err)
145			}
146			if passphraseState == keybase1.PassphraseState_RANDOM {
147				return libkb.RevokeLastDeviceError{NoPassphrase: true}
148			}
149
150			if hasPGP {
151				// even w/ forceLast, you cannot revoke your last device
152				// if you have a pgp key
153				return libkb.RevokeLastDevicePGPError{}
154			}
155
156			if !e.forceLast {
157				return libkb.RevokeLastDeviceError{}
158			}
159		}
160
161		if e.deviceID == currentDevice && !(e.forceSelf || e.forceLast) {
162			return libkb.RevokeCurrentDeviceError{}
163		}
164
165	}
166
167	kidsToRevoke, err := e.getKIDsToRevoke(me)
168	if err != nil {
169		return err
170	}
171
172	lease, merkleRoot, err := libkb.RequestDowngradeLeaseByKID(m.Ctx(), m.G(), kidsToRevoke)
173	if err != nil {
174		return err
175	}
176
177	m.UIs().LogUI.Info("Revoking KIDs:")
178	for _, kid := range kidsToRevoke {
179		m.UIs().LogUI.Info("  %s", kid)
180	}
181
182	sigKey, encKey, err := e.getDeviceSecretKeys(m, me)
183	if err != nil {
184		return err
185	}
186
187	if encKey == nil {
188		return errors.New("Missing encryption key")
189	}
190
191	var pukBoxes []keybase1.PerUserKeyBox
192	var pukPrev *libkb.PerUserKeyPrev
193
194	// Whether this run is creating a new per-user-key. Set later.
195	addingNewPUK := false
196
197	// Only used if addingNewPUK
198	var newPukGeneration keybase1.PerUserKeyGeneration
199	var newPukSeed *libkb.PerUserKeySeed
200
201	pukring, err := e.G().GetPerUserKeyring(m.Ctx())
202	if err != nil {
203		return err
204	}
205	err = pukring.Sync(m)
206	if err != nil {
207		return err
208	}
209	if pukring.HasAnyKeys() {
210		addingNewPUK = true
211		newPukGeneration = pukring.CurrentGeneration() + 1
212
213		// Make a new per-user-key
214		newPukSeedInner, err := libkb.GeneratePerUserKeySeed()
215		if err != nil {
216			return err
217		}
218		newPukSeed = &newPukSeedInner
219
220		// Create a prev secretbox containing the previous generation seed
221		pukPrevInner, err := pukring.PreparePrev(m, *newPukSeed, newPukGeneration)
222		if err != nil {
223			return err
224		}
225		pukPrev = &pukPrevInner
226
227		// Get the receivers who will be able to decrypt the new per-user-key
228		pukReceivers, err := e.getPukReceivers(m, me, kidsToRevoke)
229		if err != nil {
230			return err
231		}
232
233		// Create boxes of the new per-user-key
234		pukBoxesInner, err := pukring.PrepareBoxesForDevices(m,
235			*newPukSeed, newPukGeneration, pukReceivers, encKey)
236		if err != nil {
237			return err
238		}
239		pukBoxes = pukBoxesInner
240	}
241
242	var sigsList []libkb.JSONPayload
243
244	// Push the per-user-key sig
245
246	// Seqno when the per-user-key will be signed in.
247	var newPukSeqno keybase1.Seqno
248	if addingNewPUK {
249		sig1, err := libkb.PerUserKeyProofReverseSigned(m, me, *newPukSeed, newPukGeneration, sigKey)
250		if err != nil {
251			return err
252		}
253		newPukSeqno = me.GetSigChainLastKnownSeqno()
254		sigsList = append(sigsList, sig1.Payload)
255	}
256
257	// Push the revoke sig
258	sig2, lastSeqno, lastLinkID, err := e.makeRevokeSig(m, me, sigKey, kidsToRevoke, deviceID, merkleRoot)
259	if err != nil {
260		return err
261	}
262	sigsList = append(sigsList, sig2)
263
264	payload := make(libkb.JSONPayload)
265	payload["sigs"] = sigsList
266	payload["downgrade_lease_id"] = lease.LeaseID
267
268	if addingNewPUK {
269		libkb.AddPerUserKeyServerArg(payload, newPukGeneration, pukBoxes, pukPrev)
270	}
271
272	var myUserEKBox *keybase1.UserEkBoxed
273	var newUserEKMetadata *keybase1.UserEkMetadata
274	ekLib := e.G().GetEKLib()
275	if !e.skipUserEKForTesting && addingNewPUK && ekLib != nil {
276		sig, boxes, newMetadata, myBox, err := ekLib.PrepareNewUserEK(m, *merkleRoot, *newPukSeed)
277		if err != nil {
278			return err
279		}
280		// The assembled set of boxes includes one for the device we're in the
281		// middle of revoking. Filter it out.
282		filteredBoxes := []keybase1.UserEkBoxMetadata{}
283		deviceIDToFilter := e.explicitOrImplicitDeviceID(me)
284		for _, boxMetadata := range boxes {
285			if !boxMetadata.RecipientDeviceID.Eq(deviceIDToFilter) {
286				filteredBoxes = append(filteredBoxes, boxMetadata)
287			}
288		}
289		// If there are no active deviceEKs, we can't publish this key. This
290		// should mostly only come up in tests.
291		if len(filteredBoxes) > 0 {
292			myUserEKBox = myBox
293			newUserEKMetadata = &newMetadata
294			userEKSection := make(libkb.JSONPayload)
295			userEKSection["sig"] = sig
296			userEKSection["boxes"] = filteredBoxes
297			payload["user_ek"] = userEKSection
298		} else {
299			m.Debug("skipping userEK publishing, there are no valid deviceEKs")
300		}
301	}
302	m.Debug("RevokeEngine#Run pukBoxes:%v pukPrev:%v for generation %v, userEKBox: %v",
303		len(pukBoxes), pukPrev != nil, newPukGeneration, myUserEKBox != nil)
304
305	_, err = m.G().API.PostJSON(m, libkb.APIArg{
306		Endpoint:    "key/multi",
307		SessionType: libkb.APISessionTypeREQUIRED,
308		JSONPayload: payload,
309	})
310	if err != nil {
311		// Revoke failed, let's clear downgrade lease so it will not prevent us
312		// from trying again for given kids.
313		err2 := libkb.CancelDowngradeLease(m.Ctx(), m.G(), lease.LeaseID)
314		if err2 != nil {
315			m.Warning("Failed to cancel downgrade leases after a failed revoke: %s", err2)
316			return libkb.CombineErrors(err, err2)
317		}
318		return err
319	}
320	if err = libkb.MerkleCheckPostedUserSig(m, me.GetUID(), lastSeqno, lastLinkID); err != nil {
321		return err
322	}
323
324	if addingNewPUK {
325		err = pukring.AddKey(m, newPukGeneration, newPukSeqno, *newPukSeed)
326		if err != nil {
327			return err
328		}
329	}
330
331	// Add the new userEK box to local storage, if it was created above.
332	if myUserEKBox != nil {
333		err = e.G().GetUserEKBoxStorage().Put(m, newUserEKMetadata.Generation, *myUserEKBox)
334		if err != nil {
335			m.Warning("error while saving userEK box: %s", err)
336		}
337	}
338
339	e.G().UserChanged(m.Ctx(), me.GetUID())
340
341	return nil
342}
343
344// Get the full keys for this device.
345// Returns (sigKey, encKey, err)
346func (e *RevokeEngine) getDeviceSecretKeys(m libkb.MetaContext, me *libkb.User) (libkb.GenericKey, libkb.GenericKey, error) {
347	skaSig := libkb.SecretKeyArg{
348		Me:      me,
349		KeyType: libkb.DeviceSigningKeyType,
350	}
351	sigKey, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(skaSig, "to revoke another key"))
352	if err != nil {
353		return nil, nil, err
354	}
355	if err = sigKey.CheckSecretKey(); err != nil {
356		return nil, nil, err
357	}
358
359	skaEnc := libkb.SecretKeyArg{
360		Me:      me,
361		KeyType: libkb.DeviceEncryptionKeyType,
362	}
363	encKey, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(skaEnc, "to revoke another key"))
364	if err != nil {
365		return nil, nil, err
366	}
367	if err = encKey.CheckSecretKey(); err != nil {
368		return nil, nil, err
369	}
370
371	return sigKey, encKey, err
372}
373
374func (e *RevokeEngine) makeRevokeSig(m libkb.MetaContext, me *libkb.User, sigKey libkb.GenericKey,
375	kidsToRevoke []keybase1.KID, deviceID keybase1.DeviceID,
376	merkleRoot *libkb.MerkleRoot) (libkb.JSONPayload, keybase1.Seqno, libkb.LinkID, error) {
377	proof, err := me.RevokeKeysProof(m, sigKey, kidsToRevoke, deviceID, merkleRoot)
378	if err != nil {
379		return nil, 0, nil, err
380	}
381	sig, _, linkID, err := libkb.SignJSON(proof.J, sigKey)
382	if err != nil {
383		return nil, 0, nil, err
384	}
385
386	sig1 := make(libkb.JSONPayload)
387	sig1["sig"] = sig
388	sig1["signing_kid"] = sigKey.GetKID().String()
389	sig1["type"] = libkb.LinkTypeRevoke
390	return sig1, proof.Seqno, linkID, nil
391}
392
393// Get the receivers of the new per-user-key boxes.
394// Includes all device subkeys except any being revoked by this engine.
395func (e *RevokeEngine) getPukReceivers(m libkb.MetaContext, me *libkb.User, exclude []keybase1.KID) (res []libkb.NaclDHKeyPair, err error) {
396	excludeMap := make(map[keybase1.KID]bool)
397	for _, kid := range exclude {
398		excludeMap[kid] = true
399	}
400
401	ckf := me.GetComputedKeyFamily()
402
403	for _, dev := range ckf.GetAllActiveDevices() {
404		keyGeneric, err := ckf.GetEncryptionSubkeyForDevice(dev.ID)
405		if err != nil {
406			return nil, err
407		}
408		if !excludeMap[keyGeneric.GetKID()] {
409			key, ok := keyGeneric.(libkb.NaclDHKeyPair)
410			if !ok {
411				return nil, fmt.Errorf("Unexpected encryption key type: %T", keyGeneric)
412			}
413			res = append(res, key)
414		}
415	}
416	return res, nil
417}
418