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	"encoding/base64"
8	"errors"
9	"fmt"
10	"net/url"
11	"time"
12
13	"github.com/keybase/client/go/kbcrypto"
14	"github.com/keybase/client/go/kex2"
15	"github.com/keybase/client/go/libkb"
16	"github.com/keybase/client/go/logger"
17	"github.com/keybase/client/go/msgpack"
18	keybase1 "github.com/keybase/client/go/protocol/keybase1"
19	"github.com/keybase/go-framed-msgpack-rpc/rpc"
20	jsonw "github.com/keybase/go-jsonw"
21	"golang.org/x/net/context"
22)
23
24// Kex2Provisionee is an engine.
25type Kex2Provisionee struct {
26	libkb.Contextified
27	device       *libkb.Device
28	secret       kex2.Secret
29	secretCh     chan kex2.Secret
30	eddsa        libkb.NaclKeyPair
31	dh           libkb.NaclKeyPair
32	uid          keybase1.UID
33	username     string
34	sessionToken keybase1.SessionToken
35	csrfToken    keybase1.CsrfToken
36	pps          keybase1.PassphraseStream
37	lks          *libkb.LKSec
38	kex2Cancel   func()
39	mctx         libkb.MetaContext
40	salt         []byte
41	ekReboxer    *ephemeralKeyReboxer
42	expectedUID  keybase1.UID
43}
44
45// Kex2Provisionee implements kex2.Provisionee, libkb.UserBasic,
46// and libkb.APITokener interfaces.
47var _ kex2.Provisionee = (*Kex2Provisionee)(nil)
48var _ libkb.UserBasic = (*Kex2Provisionee)(nil)
49var _ libkb.APITokener = (*Kex2Provisionee)(nil)
50
51// NewKex2Provisionee creates a Kex2Provisionee engine.
52func NewKex2Provisionee(g *libkb.GlobalContext, device *libkb.Device, secret kex2.Secret,
53	expectedUID keybase1.UID, salt []byte) *Kex2Provisionee {
54	return &Kex2Provisionee{
55		Contextified: libkb.NewContextified(g),
56		device:       device,
57		secret:       secret,
58		secretCh:     make(chan kex2.Secret),
59		salt:         salt,
60		expectedUID:  expectedUID,
61	}
62}
63
64// Name is the unique engine name.
65func (e *Kex2Provisionee) Name() string {
66	return "Kex2Provisionee"
67}
68
69// GetPrereqs returns the engine prereqs.
70func (e *Kex2Provisionee) Prereqs() Prereqs {
71	return Prereqs{}
72}
73
74func (e *Kex2Provisionee) GetLKSec() *libkb.LKSec {
75	return e.lks
76}
77
78// RequiredUIs returns the required UIs.
79func (e *Kex2Provisionee) RequiredUIs() []libkb.UIKind {
80	return []libkb.UIKind{
81		libkb.ProvisionUIKind,
82	}
83}
84
85// SubConsumers returns the other UI consumers for this engine.
86func (e *Kex2Provisionee) SubConsumers() []libkb.UIConsumer {
87	return nil
88}
89
90type kex2LogContext struct {
91	log logger.Logger
92}
93
94func (k kex2LogContext) Debug(format string, args ...interface{}) {
95	k.log.Debug(format, args...)
96}
97
98func newKex2LogContext(g *libkb.GlobalContext) kex2LogContext {
99	return kex2LogContext{g.Log}
100}
101
102// Run starts the engine.
103func (e *Kex2Provisionee) Run(m libkb.MetaContext) error {
104	m.G().LocalSigchainGuard().Set(m.Ctx(), "Kex2Provisionee")
105	defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisionee")
106
107	// check device struct:
108	if e.device.Type == keybase1.DeviceTypeV2_NONE {
109		return errors.New("provisionee device requires Type to be set")
110	}
111	if e.device.ID.IsNil() {
112		return errors.New("provisionee device requires ID to be set")
113	}
114
115	if m.LoginContext() == nil {
116		return errors.New("Kex2Provisionee needs LoginContext set in engine.Context")
117	}
118
119	if len(e.secret) == 0 {
120		panic("empty secret")
121	}
122
123	m, e.kex2Cancel = m.WithContextCancel()
124	defer e.kex2Cancel()
125
126	// The MetaContext m is needed in some of the kex2 functions. Make sure to do that
127	// after we've added a cancelation above.
128	e.mctx = m
129
130	karg := kex2.KexBaseArg{
131		Ctx:           m.Ctx(),
132		LogCtx:        newKex2LogContext(m.G()),
133		Mr:            libkb.NewKexRouter(m),
134		DeviceID:      e.device.ID,
135		Secret:        e.secret,
136		SecretChannel: e.secretCh,
137		Timeout:       60 * time.Minute,
138	}
139	parg := kex2.ProvisioneeArg{
140		KexBaseArg:  karg,
141		Provisionee: e,
142	}
143	return kex2.RunProvisionee(parg)
144}
145
146// Cancel cancels the kex2 run if it is running.
147func (e *Kex2Provisionee) Cancel() {
148	if e.kex2Cancel == nil {
149		return
150	}
151	e.kex2Cancel()
152}
153
154// AddSecret inserts a received secret into the provisionee's
155// secret channel.
156func (e *Kex2Provisionee) AddSecret(s kex2.Secret) {
157	e.secretCh <- s
158}
159
160// GetLogFactory implements GetLogFactory in kex2.Provisionee.
161func (e *Kex2Provisionee) GetLogFactory() rpc.LogFactory {
162	return rpc.NewSimpleLogFactory(e.G().Log, nil)
163}
164
165// GetNetworkInstrumenter implements GetNetworkInstrumenter in kex2.Provisionee.
166func (e *Kex2Provisionee) GetNetworkInstrumenter() rpc.NetworkInstrumenterStorage {
167	return e.G().RemoteNetworkInstrumenterStorage
168}
169
170// HandleHello implements HandleHello in kex2.Provisionee.
171func (e *Kex2Provisionee) HandleHello(_ context.Context, harg keybase1.HelloArg) (res keybase1.HelloRes, err error) {
172	m := e.mctx
173	defer m.Trace("Kex2Provisionee#HandleHello", &err)()
174	e.pps = harg.Pps
175	res, err = e.handleHello(m, harg.Uid, harg.Token, harg.Csrf, harg.SigBody)
176	return res, err
177}
178
179func (e *Kex2Provisionee) handleHello(m libkb.MetaContext, uid keybase1.UID, token keybase1.SessionToken, csrf keybase1.CsrfToken, sigBody string) (res keybase1.HelloRes, err error) {
180
181	// save parts of the hello arg for later:
182	e.uid = uid
183	e.sessionToken = token
184	e.csrfToken = csrf
185
186	jw, err := jsonw.Unmarshal([]byte(sigBody))
187	if err != nil {
188		return res, err
189	}
190
191	// need the username later:
192	e.username, err = jw.AtPath("body.key.username").GetString()
193	if err != nil {
194		return res, err
195	}
196
197	if e.uid != e.expectedUID {
198		m.Debug("Unexpected UID in handleHello: wanted %s, got: %s", e.expectedUID, e.uid)
199		m.Debug("Username from the signature is: %q", e.username)
200		return res, fmt.Errorf("Provisioner is a different user than we wanted.")
201	}
202
203	e.eddsa, err = libkb.GenerateNaclSigningKeyPair()
204	if err != nil {
205		return res, err
206	}
207
208	e.dh, err = libkb.GenerateNaclDHKeyPair()
209	if err != nil {
210		return res, err
211	}
212
213	e.ekReboxer = newEphemeralKeyReboxer()
214
215	if err = e.addDeviceSibkey(m, jw); err != nil {
216		return res, err
217	}
218
219	if err = e.reverseSig(jw); err != nil {
220		return res, err
221	}
222
223	out, err := jw.Marshal()
224	if err != nil {
225		return res, err
226	}
227
228	return keybase1.HelloRes(out), err
229}
230
231// HandleHello2 implements HandleHello2 in kex2.Provisionee.
232func (e *Kex2Provisionee) HandleHello2(_ context.Context, harg keybase1.Hello2Arg) (res keybase1.Hello2Res, err error) {
233	m := e.mctx
234	defer m.Trace("Kex2Provisionee#HandleHello2()", &err)()
235	var res1 keybase1.HelloRes
236	res1, err = e.handleHello(m, harg.Uid, harg.Token, harg.Csrf, harg.SigBody)
237	if err != nil {
238		return res, err
239	}
240	res.SigPayload = res1
241	res.EncryptionKey = e.dh.GetKID()
242	res.DeviceEkKID, err = e.ekReboxer.getDeviceEKKID(m)
243	if err != nil {
244		return res, err
245	}
246	return res, err
247}
248
249func (e *Kex2Provisionee) HandleDidCounterSign2(_ context.Context, arg keybase1.DidCounterSign2Arg) (err error) {
250	mctx := e.mctx
251	defer mctx.Trace("Kex2Provisionee#HandleDidCounterSign2()", &err)()
252	var ppsBytes []byte
253	ppsBytes, _, err = e.dh.DecryptFromString(arg.PpsEncrypted)
254	if err != nil {
255		mctx.Debug("| Failed to decrypt pps: %s", err)
256		return err
257	}
258	err = msgpack.Decode(&e.pps, ppsBytes)
259	if err != nil {
260		mctx.Debug("| Failed to unpack pps: %s", err)
261		return err
262	}
263	return e.handleDidCounterSign(mctx, arg.Sig, arg.PukBox, arg.UserEkBox)
264}
265
266// HandleDidCounterSign implements HandleDidCounterSign in
267// kex2.Provisionee interface.
268func (e *Kex2Provisionee) HandleDidCounterSign(_ context.Context, sig []byte) (err error) {
269	return e.handleDidCounterSign(e.mctx, sig, nil, nil)
270}
271
272func (e *Kex2Provisionee) handleDidCounterSign(m libkb.MetaContext, sig []byte, perUserKeyBox *keybase1.PerUserKeyBox, userEKBox *keybase1.UserEkBoxed) (err error) {
273
274	defer m.Trace("Kex2Provisionee#handleDidCounterSign()", &err)()
275
276	// load self user (to load merkle root)
277	m.Debug("| running for username %s", e.username)
278	loadArg := libkb.NewLoadUserArgWithMetaContext(m).WithName(e.username)
279	var me *libkb.User
280	me, err = libkb.LoadUser(loadArg)
281	if err != nil {
282		return err
283	}
284	uv := me.ToUserVersion()
285	if !uv.Uid.Equal(e.uid) {
286		return fmt.Errorf("Wrong user for key exchange: %v != %v", uv.Uid, e.uid)
287	}
288
289	// decode sig
290	decSig, err := e.decodeSig(sig)
291	if err != nil {
292		return err
293	}
294
295	// make a keyproof for the dh key, signed w/ e.eddsa
296	dhSig, dhSigID, err := e.dhKeyProof(m, e.dh, decSig.eldestKID, decSig.seqno, decSig.linkID)
297	if err != nil {
298		return err
299	}
300
301	// create the key args for eddsa, dh keys
302	eddsaArgs, err := makeKeyArgs(decSig.sigID, sig, libkb.DelegationTypeSibkey, e.eddsa, decSig.eldestKID, decSig.signingKID)
303	if err != nil {
304		return err
305	}
306	dhArgs, err := makeKeyArgs(dhSigID, []byte(dhSig), libkb.DelegationTypeSubkey, e.dh, decSig.eldestKID, e.eddsa.GetKID())
307	if err != nil {
308		return err
309	}
310
311	// logged in, so update our temporary session to say so
312	if err = e.updateTemporarySession(m, uv); err != nil {
313		return err
314	}
315
316	// push the LKS server half
317	if err = e.pushLKSServerHalf(m); err != nil {
318		return err
319	}
320
321	// save device keys locally
322	if err = e.saveKeys(m); err != nil {
323		return err
324	}
325
326	if err := retryOnEphemeralRace(m, func(m libkb.MetaContext) error {
327		// Finish the ephemeral key generation -- create a deviceEKStatement and
328		// prepare the boxMetadata for posting if we received a valid userEKBox
329		reboxArg, err := e.ekReboxer.getReboxArg(m, userEKBox, e.device.ID, e.eddsa)
330		if err != nil {
331			return err
332		}
333
334		// post the key sigs to the api server
335		if err = e.postSigs(eddsaArgs, dhArgs, perUserKeyBox, reboxArg); err != nil {
336			return err
337		}
338		return nil
339	}); err != nil {
340		return err
341	}
342
343	// update the global active device, and also store the device keys in memory under ActiveDevice
344	if err = e.saveConfig(m, uv); err != nil {
345		return err
346	}
347
348	// Store the ephemeralkeys, if any. If this fails after we have
349	// posted the client will not have access to the userEK it was
350	// just reboxed for unfortunately. Without any EKs, the normal
351	// generation machinery will take over and they will make a new
352	// userEK.
353	if err := e.ekReboxer.storeEKs(m); err != nil {
354		// Swallow the error - provisioning has already happened and
355		// we've already save the config, there's no going back.
356		m.Debug("Unable to store EKs: %s", err)
357	}
358
359	return nil
360}
361
362// updateTemporarySession commits the session token and csrf token to our temporary session,
363// stored in our provisional login context. We'll need that to post successfully.
364func (e *Kex2Provisionee) updateTemporarySession(m libkb.MetaContext, uv keybase1.UserVersion) (err error) {
365	defer m.Trace("Kex2Provisionee#updateTemporarySession", &err)()
366	m.Debug("login context: %T %+v", m.LoginContext(), m.LoginContext())
367	return m.LoginContext().SaveState(string(e.sessionToken), string(e.csrfToken), libkb.NewNormalizedUsername(e.username), uv, e.device.ID)
368}
369
370type decodedSig struct {
371	sigID      keybase1.SigID
372	linkID     libkb.LinkID
373	seqno      int
374	eldestKID  keybase1.KID
375	signingKID keybase1.KID
376}
377
378func (e *Kex2Provisionee) decodeSig(sig []byte) (*decodedSig, error) {
379	body, err := base64.StdEncoding.DecodeString(string(sig))
380	if err != nil {
381		return nil, err
382	}
383	naclSig, err := kbcrypto.DecodeNaclSigInfoPacket(body)
384	if err != nil {
385		return nil, err
386	}
387	jw, err := jsonw.Unmarshal(naclSig.Payload)
388	if err != nil {
389		return nil, err
390	}
391	res := decodedSig{
392		sigID:  kbcrypto.ComputeSigIDFromSigBody(body).ToSigIDLegacy(),
393		linkID: libkb.ComputeLinkID(naclSig.Payload),
394	}
395	res.seqno, err = jw.AtKey("seqno").GetInt()
396	if err != nil {
397		return nil, err
398	}
399	seldestKID, err := jw.AtPath("body.key.eldest_kid").GetString()
400	if err != nil {
401		return nil, err
402	}
403	res.eldestKID = keybase1.KIDFromString(seldestKID)
404	ssigningKID, err := jw.AtPath("body.key.kid").GetString()
405	if err != nil {
406		return nil, err
407	}
408	res.signingKID = keybase1.KIDFromString(ssigningKID)
409
410	return &res, nil
411}
412
413// GetName implements libkb.UserBasic interface.
414func (e *Kex2Provisionee) GetName() string {
415	return e.username
416}
417
418// GetUID implements libkb.UserBasic interface.
419func (e *Kex2Provisionee) GetUID() keybase1.UID {
420	return e.uid
421}
422
423// Tokens implements the APITokener interface. This is the only implementer, but it's
424// a pretty unusual case --- the provisioned device is giving us, the provisionee,
425// a session and CSRF token to use for the server.
426func (e *Kex2Provisionee) Tokens() (token, csrf string) {
427	return string(e.sessionToken), string(e.csrfToken)
428}
429
430// Device returns the new device struct.
431func (e *Kex2Provisionee) Device() *libkb.Device {
432	return e.device
433}
434
435func (e *Kex2Provisionee) addDeviceSibkey(m libkb.MetaContext, jw *jsonw.Wrapper) error {
436	if e.device.Description == nil {
437		// should not get in here with change to login_provision.go
438		// deviceWithType that is prompting for device name before
439		// starting this engine, but leaving the code here just
440		// as a safety measure.
441
442		m.Debug("kex2 provisionee: device name (e.device.Description) is nil. It should be set by caller.")
443		m.Debug("kex2 provisionee: proceeding to prompt user for device name, but figure out how this happened...")
444
445		// need user to get existing device names
446		loadArg := libkb.NewLoadUserArgWithMetaContext(m).WithName(e.username)
447		user, err := libkb.LoadUser(loadArg)
448		if err != nil {
449			return err
450		}
451		existingDevices, err := user.DeviceNames()
452		if err != nil {
453			m.Debug("proceeding despite error getting existing device names: %s", err)
454		}
455
456		e.G().Log.Debug("kex2 provisionee: prompting for device name")
457		arg := keybase1.PromptNewDeviceNameArg{
458			ExistingDevices: existingDevices,
459		}
460		name, err := m.UIs().ProvisionUI.PromptNewDeviceName(m.Ctx(), arg)
461		if err != nil {
462			return err
463		}
464		e.device.Description = &name
465		m.Debug("kex2 provisionee: got device name: %q", name)
466	}
467
468	s := libkb.DeviceStatusActive
469	e.device.Status = &s
470	e.device.Kid = e.eddsa.GetKID()
471	dw, err := e.device.Export(libkb.LinkType(libkb.DelegationTypeSibkey))
472	if err != nil {
473		return err
474	}
475	err = jw.SetValueAtPath("body.device", dw)
476	if err != nil {
477		return err
478	}
479
480	return jw.SetValueAtPath("body.sibkey.kid", jsonw.NewString(e.eddsa.GetKID().String()))
481}
482
483func (e *Kex2Provisionee) reverseSig(jw *jsonw.Wrapper) error {
484	// need to set reverse_sig to nil before making reverse sig:
485	if err := jw.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewNil()); err != nil {
486		return err
487	}
488
489	sig, _, _, err := libkb.SignJSON(jw, e.eddsa)
490	if err != nil {
491		return err
492	}
493
494	// put the signature in reverse_sig
495	return jw.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewString(sig))
496}
497
498// postSigs takes the HTTP args for the signing key and encrypt
499// key and posts them to the api server.
500func (e *Kex2Provisionee) postSigs(signingArgs, encryptArgs *libkb.HTTPArgs,
501	perUserKeyBox *keybase1.PerUserKeyBox, reboxArg *keybase1.UserEkReboxArg) error {
502	payload := make(libkb.JSONPayload)
503	payload["sigs"] = []map[string]string{firstValues(signingArgs.ToValues()), firstValues(encryptArgs.ToValues())}
504
505	// Post the per-user-secret encrypted for the provisionee device by the provisioner.
506	if perUserKeyBox != nil {
507		libkb.AddPerUserKeyServerArg(payload, perUserKeyBox.Generation, []keybase1.PerUserKeyBox{*perUserKeyBox}, nil)
508	}
509
510	libkb.AddUserEKReBoxServerArg(payload, reboxArg)
511
512	mctx := e.mctx.WithAPITokener(e)
513	arg := libkb.APIArg{
514		Endpoint:    "key/multi",
515		SessionType: libkb.APISessionTypeREQUIRED,
516		JSONPayload: payload,
517	}
518	// MerkleCheckPostedUserSig was not added here. Changing kex2 is risky and there's no obvious attack.
519
520	_, err := e.G().API.PostJSON(mctx, arg)
521	return err
522}
523
524func makeKeyArgs(sigID keybase1.SigID, sig []byte, delType libkb.DelegationType, key libkb.GenericKey, eldestKID, signingKID keybase1.KID) (*libkb.HTTPArgs, error) {
525	pub, err := key.Encode()
526	if err != nil {
527		return nil, err
528	}
529	args := libkb.HTTPArgs{
530		"sig_id_base":     libkb.S{Val: sigID.StripSuffix().String()},
531		"sig_id_short":    libkb.S{Val: sigID.ToShortID()},
532		"sig":             libkb.S{Val: string(sig)},
533		"type":            libkb.S{Val: string(delType)},
534		"is_remote_proof": libkb.B{Val: false},
535		"public_key":      libkb.S{Val: pub},
536		"eldest_kid":      libkb.S{Val: eldestKID.String()},
537		"signing_kid":     libkb.S{Val: signingKID.String()},
538	}
539	return &args, nil
540}
541
542func (e *Kex2Provisionee) dhKeyProof(m libkb.MetaContext, dh libkb.GenericKey, eldestKID keybase1.KID, seqno int, linkID libkb.LinkID) (sig string, sigID keybase1.SigID, err error) {
543	delg := libkb.Delegator{
544		ExistingKey:    e.eddsa,
545		NewKey:         dh,
546		DelegationType: libkb.DelegationTypeSubkey,
547		Expire:         libkb.NaclDHExpireIn,
548		EldestKID:      eldestKID,
549		Device:         e.device,
550		Seqno:          keybase1.Seqno(seqno) + 1,
551		PrevLinkID:     linkID,
552		SigningUser:    e,
553		Contextified:   libkb.NewContextified(e.G()),
554	}
555
556	jw, err := libkb.KeyProof(m, delg)
557	if err != nil {
558		return "", "", err
559	}
560
561	e.G().Log.Debug("dh key proof: %s", jw.MarshalPretty())
562
563	dhSig, dhSigID, _, err := libkb.SignJSON(jw, e.eddsa)
564	if err != nil {
565		return "", "", err
566	}
567
568	return dhSig, dhSigID.ToSigIDLegacy(), nil
569
570}
571
572func (e *Kex2Provisionee) pushLKSServerHalf(m libkb.MetaContext) (err error) {
573	defer m.Trace("Kex2Provisionee#pushLKSServerHalf", &err)()
574
575	// make new lks
576	ppstream := libkb.NewPassphraseStream(e.pps.PassphraseStream)
577	ppstream.SetGeneration(libkb.PassphraseGeneration(e.pps.Generation))
578	e.lks = libkb.NewLKSec(ppstream, e.uid)
579	err = e.lks.GenerateServerHalf()
580	if err != nil {
581		return err
582	}
583
584	// make client half recovery
585	chrKID := e.dh.GetKID()
586	chrText, err := e.lks.EncryptClientHalfRecovery(e.dh)
587	if err != nil {
588		return err
589	}
590
591	err = libkb.PostDeviceLKS(m.WithAPITokener(e), e.device.ID, e.device.Type, e.lks.GetServerHalf(), e.lks.Generation(), chrText, chrKID)
592	if err != nil {
593		return err
594	}
595
596	// Sync the LKS stuff back from the server, so that subsequent
597	// attempts to use public key login will work.
598	if err = m.LoginContext().RunSecretSyncer(m, e.uid); err != nil {
599		return err
600	}
601
602	// Cache the passphrase stream.  Note that we don't have the triplesec
603	// portion of the stream cache, and that the only bytes in ppstream
604	// are the lksec portion (no pwhash, eddsa, dh).  Currently passes
605	// all tests with this situation and code that uses those portions
606	// looks to be ok.
607	m.LoginContext().CreateStreamCache(nil, ppstream)
608
609	return nil
610}
611
612// saveKeys writes the device keys to LKSec.
613func (e *Kex2Provisionee) saveKeys(m libkb.MetaContext) error {
614	_, err := libkb.WriteLksSKBToKeyring(m, e.eddsa, e.lks)
615	if err != nil {
616		return err
617	}
618	_, err = libkb.WriteLksSKBToKeyring(m, e.dh, e.lks)
619	if err != nil {
620		return err
621	}
622	return nil
623}
624
625// cacheKeys caches the device keys in the Account object.
626func (e *Kex2Provisionee) saveConfig(m libkb.MetaContext, uv keybase1.UserVersion) (err error) {
627	defer m.Trace("Kex2Provisionee#saveConfig", &err)()
628	if e.eddsa == nil {
629		return errors.New("cacheKeys called, but eddsa key is nil")
630	}
631	if e.dh == nil {
632		return errors.New("cacheKeys called, but dh key is nil")
633	}
634
635	var deviceName string
636	if e.device.Description != nil {
637		deviceName = *e.device.Description
638	}
639
640	return m.SwitchUserNewConfigActiveDevice(uv, libkb.NewNormalizedUsername(e.username), e.salt, e.device.ID, e.eddsa, e.dh, deviceName, libkb.KeychainModeOS)
641}
642
643func (e *Kex2Provisionee) SigningKey() (libkb.GenericKey, error) {
644	if e.eddsa == nil {
645		return nil, errors.New("provisionee missing signing key")
646	}
647	return e.eddsa, nil
648}
649
650func (e *Kex2Provisionee) EncryptionKey() (libkb.NaclDHKeyPair, error) {
651	if e.dh == nil {
652		return libkb.NaclDHKeyPair{}, errors.New("provisionee missing encryption key")
653	}
654	ret, ok := e.dh.(libkb.NaclDHKeyPair)
655	if !ok {
656		return libkb.NaclDHKeyPair{}, fmt.Errorf("provisionee encryption key unexpected type %T", e.dh)
657	}
658	return ret, nil
659}
660
661func firstValues(vals url.Values) map[string]string {
662	res := make(map[string]string)
663	for k, v := range vals {
664		res[k] = v[0]
665	}
666	return res
667}
668