github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/kex2_provisionee.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 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. 25 type 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. 47 var _ kex2.Provisionee = (*Kex2Provisionee)(nil) 48 var _ libkb.UserBasic = (*Kex2Provisionee)(nil) 49 var _ libkb.APITokener = (*Kex2Provisionee)(nil) 50 51 // NewKex2Provisionee creates a Kex2Provisionee engine. 52 func 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. 65 func (e *Kex2Provisionee) Name() string { 66 return "Kex2Provisionee" 67 } 68 69 // GetPrereqs returns the engine prereqs. 70 func (e *Kex2Provisionee) Prereqs() Prereqs { 71 return Prereqs{} 72 } 73 74 func (e *Kex2Provisionee) GetLKSec() *libkb.LKSec { 75 return e.lks 76 } 77 78 // RequiredUIs returns the required UIs. 79 func (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. 86 func (e *Kex2Provisionee) SubConsumers() []libkb.UIConsumer { 87 return nil 88 } 89 90 type kex2LogContext struct { 91 log logger.Logger 92 } 93 94 func (k kex2LogContext) Debug(format string, args ...interface{}) { 95 k.log.Debug(format, args...) 96 } 97 98 func newKex2LogContext(g *libkb.GlobalContext) kex2LogContext { 99 return kex2LogContext{g.Log} 100 } 101 102 // Run starts the engine. 103 func (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. 147 func (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. 156 func (e *Kex2Provisionee) AddSecret(s kex2.Secret) { 157 e.secretCh <- s 158 } 159 160 // GetLogFactory implements GetLogFactory in kex2.Provisionee. 161 func (e *Kex2Provisionee) GetLogFactory() rpc.LogFactory { 162 return rpc.NewSimpleLogFactory(e.G().Log, nil) 163 } 164 165 // GetNetworkInstrumenter implements GetNetworkInstrumenter in kex2.Provisionee. 166 func (e *Kex2Provisionee) GetNetworkInstrumenter() rpc.NetworkInstrumenterStorage { 167 return e.G().RemoteNetworkInstrumenterStorage 168 } 169 170 // HandleHello implements HandleHello in kex2.Provisionee. 171 func (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 179 func (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. 232 func (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 249 func (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. 268 func (e *Kex2Provisionee) HandleDidCounterSign(_ context.Context, sig []byte) (err error) { 269 return e.handleDidCounterSign(e.mctx, sig, nil, nil) 270 } 271 272 func (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. 364 func (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 370 type decodedSig struct { 371 sigID keybase1.SigID 372 linkID libkb.LinkID 373 seqno int 374 eldestKID keybase1.KID 375 signingKID keybase1.KID 376 } 377 378 func (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. 414 func (e *Kex2Provisionee) GetName() string { 415 return e.username 416 } 417 418 // GetUID implements libkb.UserBasic interface. 419 func (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. 426 func (e *Kex2Provisionee) Tokens() (token, csrf string) { 427 return string(e.sessionToken), string(e.csrfToken) 428 } 429 430 // Device returns the new device struct. 431 func (e *Kex2Provisionee) Device() *libkb.Device { 432 return e.device 433 } 434 435 func (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 483 func (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. 500 func (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 524 func 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 542 func (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 572 func (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. 613 func (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. 626 func (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 643 func (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 650 func (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 661 func 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 }