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	"time"
9
10	"github.com/keybase/client/go/kex2"
11	"github.com/keybase/client/go/libkb"
12	"github.com/keybase/client/go/msgpack"
13	keybase1 "github.com/keybase/client/go/protocol/keybase1"
14	"github.com/keybase/go-framed-msgpack-rpc/rpc"
15	jsonw "github.com/keybase/go-jsonw"
16	"golang.org/x/net/context"
17)
18
19// Kex2Provisioner is an engine.
20type Kex2Provisioner struct {
21	libkb.Contextified
22	secret                kex2.Secret
23	secretCh              chan kex2.Secret
24	me                    *libkb.User
25	signingKey            libkb.GenericKey
26	encryptionKey         libkb.NaclDHKeyPair
27	pps                   keybase1.PassphraseStream
28	provisioneeDeviceName string
29	provisioneeDeviceType keybase1.DeviceTypeV2
30	mctx                  libkb.MetaContext
31	proof                 *jsonw.Wrapper
32}
33
34// Kex2Provisioner implements kex2.Provisioner interface.
35var _ kex2.Provisioner = (*Kex2Provisioner)(nil)
36
37// NewKex2Provisioner creates a Kex2Provisioner engine.
38func NewKex2Provisioner(g *libkb.GlobalContext, secret kex2.Secret, pps *libkb.PassphraseStream) *Kex2Provisioner {
39	e := &Kex2Provisioner{
40		Contextified: libkb.NewContextified(g),
41		secret:       secret,
42		secretCh:     make(chan kex2.Secret),
43	}
44	if pps != nil {
45		e.pps = pps.Export()
46	}
47
48	return e
49}
50
51// Name is the unique engine name.
52func (e *Kex2Provisioner) Name() string {
53	return "Kex2Provisioner"
54}
55
56// GetPrereqs returns the engine prereqs.
57func (e *Kex2Provisioner) Prereqs() Prereqs {
58	return Prereqs{Device: true}
59}
60
61// RequiredUIs returns the required UIs.
62func (e *Kex2Provisioner) RequiredUIs() []libkb.UIKind {
63	return []libkb.UIKind{
64		libkb.SecretUIKind,
65		libkb.ProvisionUIKind,
66	}
67}
68
69// SubConsumers returns the other UI consumers for this engine.
70func (e *Kex2Provisioner) SubConsumers() []libkb.UIConsumer {
71	return nil
72}
73
74// Run starts the provisioner engine.
75func (e *Kex2Provisioner) Run(m libkb.MetaContext) error {
76	// The guard is acquired later, after the potentially long pause by the user.
77	defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisioner")
78
79	// before starting provisioning, need to load some information:
80	if err := e.loadMe(); err != nil {
81		return err
82	}
83	if err := m.ActiveDevice().ClearPassphraseStreamCacheIfOutdated(m); err != nil {
84		return err
85	}
86	if err := e.loadSecretKeys(m); err != nil {
87		return err
88	}
89
90	// get current passphrase stream if necessary:
91	if e.pps.PassphraseStream == nil {
92		m.Debug("kex2 provisioner needs passphrase stream, getting it via GetPassphraseStreamStored")
93		pps, err := libkb.GetPassphraseStreamStored(m)
94		if err != nil {
95			return err
96		}
97		e.pps = pps.Export()
98	}
99
100	// Go's context.Context needed by some kex2 callback functions
101	m = m.EnsureCtx()
102	e.mctx = m
103
104	deviceID := m.G().Env.GetDeviceID()
105
106	// all set:  start provisioner
107	karg := kex2.KexBaseArg{
108		Ctx:           m.Ctx(),
109		LogCtx:        newKex2LogContext(m.G()),
110		Mr:            libkb.NewKexRouter(m),
111		DeviceID:      deviceID,
112		Secret:        e.secret,
113		SecretChannel: e.secretCh,
114		Timeout:       60 * time.Minute,
115	}
116	parg := kex2.ProvisionerArg{
117		KexBaseArg:   karg,
118		Provisioner:  e,
119		HelloTimeout: 15 * time.Second,
120	}
121	if err := kex2.RunProvisioner(parg); err != nil {
122		return err
123	}
124	m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisioner")
125
126	// successfully provisioned the other device
127	sarg := keybase1.ProvisionerSuccessArg{
128		DeviceName: e.provisioneeDeviceName,
129		DeviceType: e.provisioneeDeviceType,
130	}
131	return m.UIs().ProvisionUI.ProvisionerSuccess(context.Background(), sarg)
132}
133
134func (e *Kex2Provisioner) loadSecretKeys(m libkb.MetaContext) (err error) {
135	// get signing key (including secret key)
136	ska1 := libkb.SecretKeyArg{
137		Me:      e.me,
138		KeyType: libkb.DeviceSigningKeyType,
139	}
140	e.signingKey, err = m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska1, "new device install"))
141	if err != nil {
142		return err
143	}
144
145	// get encryption key (including secret key)
146	ska2 := libkb.SecretKeyArg{
147		Me:      e.me,
148		KeyType: libkb.DeviceEncryptionKeyType,
149	}
150	encryptionKeyGeneric, err := e.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska2, "new device install"))
151	if err != nil {
152		return err
153	}
154	var ok bool
155	e.encryptionKey, ok = encryptionKeyGeneric.(libkb.NaclDHKeyPair)
156	if !ok {
157		return fmt.Errorf("Unexpected encryption key type")
158	}
159	return nil
160}
161
162// AddSecret inserts a received secret into the provisioner's
163// secret channel.
164func (e *Kex2Provisioner) AddSecret(s kex2.Secret) {
165	e.secretCh <- s
166}
167
168// GetLogFactory implements GetLogFactory in kex2.Provisioner.
169func (e *Kex2Provisioner) GetLogFactory() rpc.LogFactory {
170	return rpc.NewSimpleLogFactory(e.G().Log, nil)
171}
172
173// GetNetworkInstrumenter implements GetNetworkInstrumenter in kex2.Provisioner.
174func (e *Kex2Provisioner) GetNetworkInstrumenter() rpc.NetworkInstrumenterStorage {
175	return e.G().RemoteNetworkInstrumenterStorage
176}
177
178// GetHelloArg implements GetHelloArg in kex2.Provisioner.
179func (e *Kex2Provisioner) GetHelloArg() (arg keybase1.HelloArg, err error) {
180
181	// Pull the metaContext out of the this object, since we can't pass it through the
182	// kex2/provisioner interface
183	m := e.mctx
184
185	defer m.Trace("Kex2Provisioner#GetHelloArg()", &err)()
186
187	_ = m.UIs().ProvisionUI.DisplaySecretExchanged(context.Background(), 0)
188
189	// get a session token that device Y can use
190	mctx := libkb.NewMetaContextBackground(e.G())
191	tokener, err := libkb.NewSessionTokener(mctx)
192	if err != nil {
193		return arg, err
194	}
195	token, csrf := tokener.Tokens()
196
197	// generate a skeleton key proof
198	sigBody, err := e.skeletonProof(m)
199	if err != nil {
200		return arg, err
201	}
202
203	// return the HelloArg
204	arg = keybase1.HelloArg{
205		Uid:     e.me.GetUID(),
206		Pps:     e.pps,
207		Token:   keybase1.SessionToken(token),
208		Csrf:    keybase1.CsrfToken(csrf),
209		SigBody: sigBody,
210	}
211	return arg, nil
212}
213
214// GetHello2Arg implements GetHello2Arg in kex2.Provisioner.
215func (e *Kex2Provisioner) GetHello2Arg() (arg2 keybase1.Hello2Arg, err error) {
216	// Pull the metaContext out of the this object, since we can't pass it through the
217	// kex2/provisioner interface
218	m := e.mctx
219
220	defer m.Trace("Kex2Provisioner#GetHello2Arg", &err)()
221
222	var arg1 keybase1.HelloArg
223	arg1, err = e.GetHelloArg()
224	if err != nil {
225		return arg2, err
226	}
227
228	arg2 = keybase1.Hello2Arg{
229		Uid:     arg1.Uid,
230		Token:   arg1.Token,
231		Csrf:    arg1.Csrf,
232		SigBody: arg1.SigBody,
233	}
234	return arg2, nil
235}
236
237// CounterSign implements CounterSign in kex2.Provisioner.
238func (e *Kex2Provisioner) CounterSign(input keybase1.HelloRes) (sig []byte, err error) {
239	m := e.mctx
240	defer m.Trace("Kex2Provisioner#CounterSign", &err)()
241
242	jw, err := jsonw.Unmarshal([]byte(input))
243	if err != nil {
244		return nil, err
245	}
246
247	// check the reverse signature and put the values from the provisionee into
248	// e.proof
249	if err = e.checkReverseSig(jw); err != nil {
250		m.Debug("provisioner failed to verify reverse sig: %s", err)
251		return nil, err
252	}
253	m.Debug("provisioner verified reverse sig")
254
255	// remember some device information for ProvisionUI.ProvisionerSuccess()
256	if err = e.rememberDeviceInfo(e.proof); err != nil {
257		return nil, err
258	}
259
260	// sign the whole thing with provisioner's signing key
261	s, _, _, err := libkb.SignJSON(e.proof, e.signingKey)
262	if err != nil {
263		return nil, err
264	}
265
266	return []byte(s), nil
267}
268
269// CounterSign2 implements CounterSign in kex2.Provisioner.
270func (e *Kex2Provisioner) CounterSign2(input keybase1.Hello2Res) (output keybase1.DidCounterSign2Arg, err error) {
271
272	m := e.mctx
273
274	defer m.Trace("Kex2Provisioner#CounterSign2", &err)()
275	var key libkb.GenericKey
276	key, err = libkb.ImportKeypairFromKID(input.EncryptionKey)
277	if err != nil {
278		return output, err
279	}
280
281	output.Sig, err = e.CounterSign(input.SigPayload)
282	if err != nil {
283		return output, err
284	}
285
286	var ppsPacked []byte
287	ppsPacked, err = msgpack.Encode(e.pps)
288	if err != nil {
289		return output, err
290	}
291	output.PpsEncrypted, err = key.EncryptToString(ppsPacked, nil)
292
293	// Sync the PUK, if the pukring is nil, we don't have a PUK and have
294	// nothing to box. We also can't make a userEKBox which is signed by the
295	// PUK.
296	pukring, err := e.syncPUK(m)
297	if err != nil || pukring == nil {
298		return output, err
299	}
300
301	output.PukBox, err = e.makePukBox(m, pukring, key)
302	if err != nil {
303		return output, err
304	}
305
306	userEKBoxStorage := m.G().GetUserEKBoxStorage()
307	if input.DeviceEkKID.Exists() && userEKBoxStorage != nil {
308		// If we error out here the provisionee will create it's own keys later
309		// but we shouldn't fail kex.
310		userEKBox, ekErr := makeUserEKBoxForProvisionee(m, input.DeviceEkKID)
311		if ekErr != nil {
312			userEKBox = nil
313			m.Debug("Unable to makeUserEKBox %v", ekErr)
314		}
315		output.UserEkBox = userEKBox
316	} else {
317		m.Debug("Skipping userEKBox generation empty KID or storage. KID: %v, storage: %v", input.DeviceEkKID, userEKBoxStorage)
318	}
319
320	return output, nil
321}
322
323// skeletonProof generates a partial key proof structure that device Y can
324// fill in. When verifying the reverse signature we fill in the values from Y
325// to check the reverse signature
326func (e *Kex2Provisioner) skeletonProof(m libkb.MetaContext) (sigBody string, err error) {
327
328	// Set the local sigchain guard to tell background tasks
329	// to stay off the sigchain while we do this.
330	// This is released at the end of Kex2Provisioner#Run
331	e.G().LocalSigchainGuard().Set(context.TODO(), "Kex2Provisioner")
332
333	// reload the self user to make sure it is fresh
334	// (this fixes TestProvisionWithRevoke [CORE-5631, CORE-5636])
335	if err := e.loadMe(); err != nil {
336		return "", err
337	}
338
339	delg := libkb.Delegator{
340		ExistingKey:    e.signingKey,
341		Me:             e.me,
342		DelegationType: libkb.DelegationTypeSibkey,
343		Expire:         libkb.NaclEdDSAExpireIn,
344		Contextified:   libkb.NewContextified(e.G()),
345	}
346
347	e.proof, err = libkb.KeyProof(m, delg)
348	if err != nil {
349		return "", err
350	}
351	body, err := e.proof.Marshal()
352	if err != nil {
353		return "", err
354	}
355	return string(body), nil
356}
357
358// checkReverseSig verifies that the reverse sig in jw is valid and matches
359// e.proof. The provisionee is only allowed to pass the following fields to the
360// provisioner:
361// body.device
362// body.sibkey.kid
363// The values at these paths in the json reserialized and are inserted into the
364// skeleton proof that we initially passed to the provisionee so we can ensure
365// no other values were added when verifying the signature.
366func (e *Kex2Provisioner) checkReverseSig(jw *jsonw.Wrapper) error {
367	kid, err := jw.AtPath("body.sibkey.kid").GetString()
368	if err != nil {
369		return err
370	}
371
372	keypair, err := libkb.ImportKeypairFromKID(keybase1.KIDFromString(kid))
373	if err != nil {
374		return err
375	}
376
377	revsig, err := jw.AtPath("body.sibkey.reverse_sig").GetString()
378	if err != nil {
379		return err
380	}
381
382	// set reverse_sig to nil to verify it:
383	err = e.proof.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewNil())
384	if err != nil {
385		return err
386	}
387
388	// Copy known fields that provisionee set into e.proof
389	deviceWrapper := jw.AtPath("body.device")
390	// NOTE the time value is dropped during Export, value here is arbitrary.
391	device, err := libkb.ParseDevice(deviceWrapper, time.Now())
392	if err != nil {
393		return err
394	}
395	dw, err := device.Export(libkb.LinkType(libkb.DelegationTypeSibkey))
396	if err != nil {
397		return err
398	}
399	err = e.proof.SetValueAtPath("body.device", dw)
400	if err != nil {
401		return err
402	}
403	err = e.proof.SetValueAtPath("body.sibkey.kid", jsonw.NewString(kid))
404	if err != nil {
405		return err
406	}
407
408	msg, err := e.proof.Marshal()
409	if err != nil {
410		return err
411	}
412	_, err = keypair.VerifyString(e.G().Log, revsig, msg)
413	if err != nil {
414		return err
415	}
416
417	// put reverse_sig back in
418	return e.proof.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewString(revsig))
419}
420
421// rememberDeviceInfo saves the device name and type in
422// Kex2Provisioner for later use.
423func (e *Kex2Provisioner) rememberDeviceInfo(jw *jsonw.Wrapper) error {
424	name, err := jw.AtPath("body.device.name").GetString()
425	if err != nil {
426		return err
427	}
428	e.provisioneeDeviceName = name
429
430	dtype, err := jw.AtPath("body.device.type").GetString()
431	if err != nil {
432		return err
433	}
434	e.provisioneeDeviceType, err = keybase1.StringToDeviceTypeV2(dtype)
435
436	return err
437}
438
439// Returns nil if there are no per-user-keys.
440func (e *Kex2Provisioner) syncPUK(m libkb.MetaContext) (*libkb.PerUserKeyring, error) {
441	pukring, err := e.G().GetPerUserKeyring(m.Ctx())
442	if err != nil {
443		return nil, err
444	}
445	if err = pukring.Sync(m); err != nil {
446		return nil, err
447	}
448	if !pukring.HasAnyKeys() {
449		return nil, nil
450	}
451	return pukring, nil
452}
453
454func (e *Kex2Provisioner) makePukBox(m libkb.MetaContext, pukring *libkb.PerUserKeyring, receiverKeyGeneric libkb.GenericKey) (*keybase1.PerUserKeyBox, error) {
455	receiverKey, ok := receiverKeyGeneric.(libkb.NaclDHKeyPair)
456	if !ok {
457		return nil, fmt.Errorf("Unexpected receiver key type")
458	}
459
460	pukBox, err := pukring.PrepareBoxForNewDevice(m,
461		receiverKey,     // receiver key: provisionee enc
462		e.encryptionKey) // sender key: this device enc
463	return &pukBox, err
464}
465
466func (e *Kex2Provisioner) loadMe() error {
467	var err error
468	e.me, err = libkb.LoadMe(libkb.NewLoadUserArg(e.G()))
469	return err
470}
471