1package engine
2
3import (
4	"errors"
5	"fmt"
6
7	"github.com/keybase/client/go/libkb"
8)
9
10type SelfProvisionEngine struct {
11	libkb.Contextified
12	DeviceName     string
13	result         error
14	lks            *libkb.LKSec
15	User           *libkb.User
16	perUserKeyring *libkb.PerUserKeyring
17	ekReboxer      *ephemeralKeyReboxer
18
19	deviceWrapEng *DeviceWrap
20}
21
22// If a device is cloned, we can provision a new device from the current device
23// to get out of the cloned state.
24func NewSelfProvisionEngine(g *libkb.GlobalContext, deviceName string) *SelfProvisionEngine {
25	return &SelfProvisionEngine{
26		Contextified: libkb.NewContextified(g),
27		DeviceName:   deviceName,
28	}
29}
30
31func (e *SelfProvisionEngine) Name() string {
32	return "SelfProvision"
33}
34
35func (e *SelfProvisionEngine) Prereqs() Prereqs {
36	return Prereqs{}
37}
38
39func (e *SelfProvisionEngine) RequiredUIs() []libkb.UIKind {
40	return []libkb.UIKind{
41		libkb.ProvisionUIKind,
42		libkb.LogUIKind,
43		libkb.SecretUIKind,
44		libkb.LoginUIKind,
45	}
46}
47
48func (e *SelfProvisionEngine) SubConsumers() []libkb.UIConsumer {
49	return []libkb.UIConsumer{
50		&loginLoadUser{},
51	}
52}
53
54func (e *SelfProvisionEngine) Result() error {
55	return e.result
56}
57
58func (e *SelfProvisionEngine) Run(mctx libkb.MetaContext) (err error) {
59	defer mctx.Trace("SelfProvisionEngine#Run", &err)()
60	return retryOnEphemeralRace(mctx, e.run)
61}
62
63func (e *SelfProvisionEngine) run(m libkb.MetaContext) (err error) {
64	m.G().LocalSigchainGuard().Set(m.Ctx(), "SelfProvisionEngine")
65	defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "SelfProvisionEngine")
66
67	if d, err := libkb.GetDeviceCloneState(m); err != nil {
68		return err
69	} else if !d.IsClone() {
70		return fmt.Errorf("to self provision, you must be a cloned device")
71	}
72
73	if err = m.G().SecretStore().PrimeSecretStores(m); err != nil {
74		return SecretStoreNotFunctionalError{err}
75	}
76
77	uv, _ := e.G().ActiveDevice.GetUsernameAndUserVersionIfValid(m)
78	// Pass the UV here so the passphrase stream is cached on the provisional
79	// login context
80	m = m.WithNewProvisionalLoginContextForUserVersionAndUsername(uv, e.G().Env.GetUsername())
81
82	// From this point on, if there's an error, we abort the transaction.
83	defer func() {
84		if err == nil {
85			// cache the passphrase stream from the login context to the active
86			// device.
87			m.CommitProvisionalLogin()
88		}
89	}()
90
91	keys, err := e.loadUserAndActiveDeviceKeys(m)
92	if err != nil {
93		return err
94	}
95
96	e.ekReboxer = newEphemeralKeyReboxer()
97
98	// Make new device keys and sign them with current device keys
99	if err := e.provision(m, keys); err != nil {
100		return err
101	}
102
103	// Finish provisoning by calling SwitchConfigAndActiveDevice. we
104	// can't undo that, so do not error out after that.
105	if err := e.deviceWrapEng.SwitchConfigAndActiveDevice(m); err != nil {
106		return err
107	}
108
109	// Cleanup EKs belonging to the old device.
110	if deviceEKStorage := m.G().GetDeviceEKStorage(); deviceEKStorage != nil {
111		if err = deviceEKStorage.ForceDeleteAll(m, e.User.GetNormalizedName()); err != nil {
112			m.Debug("unable to remove old ephemeral keys: %v", err)
113		}
114	}
115
116	// Store and encrypt the new deviceEK with the new globally set
117	// active device.
118	if err := e.ekReboxer.storeEKs(m); err != nil {
119		m.Debug("unable to store ephemeral keys: %v", err)
120	}
121
122	verifyLocalStorage(m, e.User.GetNormalizedName().String(), e.User.GetUID())
123	if err := e.syncSecretStore(m); err != nil {
124		m.Debug("unable to syncSecretStore: %v", err)
125	}
126
127	e.clearCaches(m)
128	e.sendNotification(m)
129	return nil
130}
131
132func (e *SelfProvisionEngine) loadUserAndActiveDeviceKeys(m libkb.MetaContext) (*libkb.DeviceWithKeys, error) {
133	// run the LoginLoadUser sub-engine to load a user
134	ueng := newLoginLoadUser(e.G(), e.G().Env.GetUsername().String())
135	if err := RunEngine2(m, ueng); err != nil {
136		return nil, err
137	}
138	e.User = ueng.User()
139	pukRing, err := libkb.NewPerUserKeyring(e.G(), e.User.GetUID())
140	if err != nil {
141		return nil, err
142	}
143	e.perUserKeyring = pukRing
144
145	keys, err := e.G().ActiveDevice.DeviceKeys()
146	if err != nil {
147		return nil, err
148	}
149	if _, err := keys.Populate(m); err != nil {
150		return nil, err
151	}
152
153	return keys, nil
154}
155
156func (e *SelfProvisionEngine) provision(m libkb.MetaContext, keys *libkb.DeviceWithKeys) error {
157	// Set the active device to be a special provisional key active device,
158	// which keeps a cached copy around for DeviceKeyGen, which requires it to
159	// be in memory.  It also will establish a NIST so that API calls can
160	// proceed on behalf of the user.
161	m = m.WithProvisioningKeyActiveDevice(keys, e.User.ToUserVersion())
162
163	// need lksec to store device keys locally
164	if err := e.fetchLKS(m, keys.EncryptionKey()); err != nil {
165		return err
166	}
167	return e.makeDeviceKeysWithSigner(m, keys.SigningKey())
168}
169
170// copied from loginProvision
171func (e *SelfProvisionEngine) fetchLKS(m libkb.MetaContext, encKey libkb.GenericKey) error {
172	gen, clientLKS, err := fetchLKS(m, encKey)
173	if err != nil {
174		return err
175	}
176	e.lks = libkb.NewLKSecWithClientHalf(clientLKS, gen, e.User.GetUID())
177	return nil
178}
179
180// makeDeviceKeysWithSigner creates device keys given a signing key.
181func (e *SelfProvisionEngine) makeDeviceKeysWithSigner(m libkb.MetaContext, signer libkb.GenericKey) error {
182	if err := e.ensureLKSec(m); err != nil {
183		return err
184	}
185
186	_, _, deviceType, err := m.G().GetUPAKLoader().LookupUsernameAndDevice(m.Ctx(), e.User.GetUID(), e.G().ActiveDevice.DeviceID())
187	if err != nil {
188		return err
189	}
190
191	args := &DeviceWrapArgs{
192		Me:              e.User,
193		DeviceName:      e.DeviceName,
194		DeviceType:      deviceType,
195		Lks:             e.lks,
196		IsEldest:        false, // just to be explicit
197		IsSelfProvision: true,
198		PerUserKeyring:  e.perUserKeyring,
199		EldestKID:       e.User.GetEldestKID(),
200		Signer:          signer,
201		EkReboxer:       e.ekReboxer,
202	}
203
204	e.deviceWrapEng = NewDeviceWrap(m.G(), args)
205	return RunEngine2(m, e.deviceWrapEng)
206}
207
208// copied from loginProvision
209// ensureLKSec ensures we have LKSec for saving device keys.
210func (e *SelfProvisionEngine) ensureLKSec(m libkb.MetaContext) error {
211	if e.lks != nil {
212		return nil
213	}
214
215	pps, err := e.ppStream(m)
216	if err != nil {
217		return err
218	}
219
220	e.lks = libkb.NewLKSec(pps, e.User.GetUID())
221	return nil
222}
223
224// copied from loginProvision
225// ppStream gets the passphrase stream from the cache
226func (e *SelfProvisionEngine) ppStream(m libkb.MetaContext) (*libkb.PassphraseStream, error) {
227	if m.LoginContext() == nil {
228		return nil, errors.New("SelfProvisionEngine: ppStream() -> nil ctx.LoginContext")
229	}
230	cached := m.LoginContext().PassphraseStreamCache()
231	if cached == nil {
232		return nil, errors.New("SelfProvisionEngine: ppStream() -> nil PassphraseStreamCache")
233	}
234	return cached.PassphraseStream(), nil
235}
236
237func (e *SelfProvisionEngine) syncSecretStore(m libkb.MetaContext) error {
238	// now store the secrets for our new device
239	encKey, err := m.ActiveDevice().EncryptionKey()
240	if err != nil {
241		return err
242	}
243	if err := e.fetchLKS(m, encKey); err != nil {
244		return err
245	}
246
247	// Get the LKS server half.
248	if err := e.lks.Load(m); err != nil {
249		return err
250	}
251
252	options := libkb.LoadAdvisorySecretStoreOptionsFromRemote(m)
253	return libkb.StoreSecretAfterLoginWithLKSWithOptions(m, e.User.GetNormalizedName(), e.lks, &options)
254}
255
256func (e *SelfProvisionEngine) clearCaches(mctx libkb.MetaContext) {
257	// Any caches that are encrypted with the old device key should be cleared
258	// out here so we can re-populate and encrypt with the new key.
259	if _, err := e.G().LocalChatDb.Nuke(); err != nil {
260		mctx.Debug("unable to nuke LocalChatDb: %v", err)
261	}
262	if ekLib := e.G().GetEKLib(); ekLib != nil {
263		ekLib.ClearCaches(mctx)
264	}
265}
266
267func (e *SelfProvisionEngine) sendNotification(m libkb.MetaContext) {
268	e.G().KeyfamilyChanged(m.Ctx(), e.User.GetUID())
269	e.G().NotifyRouter.HandleLogin(m.Ctx(), string(e.G().Env.GetUsername()))
270}
271