func (p ProvisionUI) DisplayAndPromptSecret(ctx context.Context, arg keybase1.DisplayAndPromptSecretArg) ([]byte, error) { if p.role == libkb.KexRoleProvisioner { // This is the provisioner device (device X) // For command line app, all secrets are entered on the provisioner only: p.parent.Output("Enter the verification code from your other device here:\n\n") ret, err := PromptWithChecker(PromptDescriptorProvisionPhrase, p.parent, "Verification code", false, libkb.CheckNotEmpty) if err != nil { return nil, err } secret, err := libkb.NewKex2SecretFromPhrase(ret) if err != nil { return nil, err } sbytes := secret.Secret() return sbytes[:], nil } if p.role == libkb.KexRoleProvisionee { // this is the provisionee device (device Y) // For command line app, the provisionee displays secrets only p.parent.Output("Type this verification code into your other device:\n\n") p.parent.Output("\t" + arg.Phrase + "\n\n") p.parent.Output("If you are using the command line client on your other device, run this command:\n\n") p.parent.Output("\tkeybase device add\n\n") p.parent.Output("It will then prompt you for the verification code above.\n\n") if arg.OtherDeviceType == keybase1.DeviceType_MOBILE { encodings, err := qrcode.Encode(arg.Secret) // ignoring any of these errors...phrase above will suffice. if err == nil { p.parent.Output("Or, scan this QR Code with the keybase app on your mobile phone:\n\n") p.parent.Output(encodings.Terminal) fname := path.Join(os.TempDir(), "keybase_qr.png") f, ferr := os.Create(fname) if ferr == nil { f.Write(encodings.PNG) f.Close() p.parent.Printf("\nThere's also a PNG version in %s that might work better.\n\n", fname) } } } return nil, nil } return nil, libkb.InvalidArgumentError{Msg: fmt.Sprintf("invalid ProvisionUI role: %d", p.role)} }
func (p ProvisionUI) DisplayAndPromptSecret(ctx context.Context, arg keybase1.DisplayAndPromptSecretArg) ([]byte, error) { // Display the secret: // TODO: if arg.OtherDeviceType == keybase1.DeviceType_MOBILE { show qr code instead } p.parent.Output("Type this verification code into your other device:\n\n") p.parent.Output("\t" + arg.Phrase + "\n\n") // Prompt for the secret from the other device: p.parent.Output("\n -- Or -- \n") p.parent.Output("Enter the verification code from your other device here:\n\n") ret, err := PromptWithChecker(PromptDescriptorProvisionPhrase, p.parent, "Verification code", false, libkb.CheckNotEmpty) if err != nil { return nil, err } secret, err := libkb.NewKex2SecretFromPhrase(ret) if err != nil { return nil, err } sbytes := secret.Secret() return sbytes[:], nil /* } if arg.OtherDeviceType == keybase1.DeviceType_DESKTOP { p.parent.Output("Type this verification code into your other device:\n\n") p.parent.Output("\t" + arg.Phrase + "\n") // in C2 > C1 flow, there's no secret input on C2 // (computer -> computer provisioning, device Y (provisionee) does not // offer to accept a secret from device X (provisioner) even though // the protocol allows it.) return nil, nil } return nil, fmt.Errorf("invalid device type: %d", arg.OtherDeviceType) */ }
// Run starts the engine. func (e *DeviceAdd) Run(ctx *Context) (err error) { e.G().Log.Debug("+ DeviceAdd.Run()") defer func() { e.G().Log.Debug("- DeviceAdd.Run() -> %s", libkb.ErrToOk(err)) }() arg := keybase1.ChooseDeviceTypeArg{Kind: keybase1.ChooseType_NEW_DEVICE} provisioneeType, err := ctx.ProvisionUI.ChooseDeviceType(context.TODO(), arg) if err != nil { return err } e.G().Log.Debug("provisionee device type: %v", provisioneeType) // make a new secret: secret, err := libkb.NewKex2Secret() if err != nil { return err } e.G().Log.Debug("secret phrase: %s", secret.Phrase()) // provisioner needs ppstream, and UI is confusing when it asks for // it at the same time as asking for the secret, so get it first // before prompting for the kex2 secret: pps, err := e.G().LoginState().GetPassphraseStream(ctx.SecretUI) if err != nil { return err } // create provisioner engine provisioner := NewKex2Provisioner(e.G(), secret.Secret(), pps) var canceler func() // display secret and prompt for secret from X in a goroutine: go func() { sb := secret.Secret() arg := keybase1.DisplayAndPromptSecretArg{ Secret: sb[:], Phrase: secret.Phrase(), OtherDeviceType: provisioneeType, } var contxt context.Context contxt, canceler = context.WithCancel(context.Background()) receivedSecret, err := ctx.ProvisionUI.DisplayAndPromptSecret(contxt, arg) if err != nil { // XXX ??? e.G().Log.Warning("DisplayAndPromptSecret error: %s", err) } else if receivedSecret.Secret != nil && len(receivedSecret.Secret) > 0 { e.G().Log.Debug("received secret, adding to provisioner") var ks kex2.Secret copy(ks[:], receivedSecret.Secret) provisioner.AddSecret(ks) } else if len(receivedSecret.Phrase) > 0 { e.G().Log.Debug("received secret phrase, adding to provisioner") ks, err := libkb.NewKex2SecretFromPhrase(receivedSecret.Phrase) if err != nil { e.G().Log.Warning("DisplayAndPromptSecret error: %s", err) } else { provisioner.AddSecret(ks.Secret()) } } }() defer func() { if canceler != nil { e.G().Log.Debug("canceling DisplayAndPromptSecret call") canceler() } }() // run provisioner if err = RunEngine(provisioner, ctx); err != nil { if err == kex2.ErrHelloTimeout { return libkb.CanceledError{M: "Failed to provision device: are you sure you typed the secret properly?"} } return err } return nil }
// deviceWithType provisions this device with an existing device using the // kex2 protocol. provisionerType is the existing device type. func (e *loginProvision) deviceWithType(ctx *Context, provisionerType keybase1.DeviceType) error { // make a new secret: secret, err := libkb.NewKex2Secret() if err != nil { return err } e.G().Log.Debug("secret phrase received") // make a new device: deviceID, err := libkb.NewDeviceID() if err != nil { return err } device := &libkb.Device{ ID: deviceID, Type: e.arg.DeviceType, } // create provisionee engine provisionee := NewKex2Provisionee(e.G(), device, secret.Secret()) var canceler func() // display secret and prompt for secret from X in a goroutine: go func() { sb := secret.Secret() arg := keybase1.DisplayAndPromptSecretArg{ Secret: sb[:], Phrase: secret.Phrase(), OtherDeviceType: provisionerType, } var contxt context.Context contxt, canceler = context.WithCancel(context.Background()) receivedSecret, err := ctx.ProvisionUI.DisplayAndPromptSecret(contxt, arg) if err != nil { // cancel provisionee run: provisionee.Cancel() e.G().Log.Warning("DisplayAndPromptSecret error: %s", err) } else if receivedSecret.Secret != nil && len(receivedSecret.Secret) > 0 { e.G().Log.Debug("received secret, adding to provisionee") var ks kex2.Secret copy(ks[:], receivedSecret.Secret) provisionee.AddSecret(ks) } else if len(receivedSecret.Phrase) > 0 { e.G().Log.Debug("received secret phrase, adding to provisionee") ks, err := libkb.NewKex2SecretFromPhrase(receivedSecret.Phrase) if err != nil { e.G().Log.Warning("DisplayAndPromptSecret error: %s", err) } else { provisionee.AddSecret(ks.Secret()) } } }() defer func() { if canceler != nil { e.G().Log.Debug("canceling DisplayAndPromptSecret call") canceler() } }() f := func(lctx libkb.LoginContext) error { // run provisionee ctx.LoginContext = lctx return RunEngine(provisionee, ctx) } if err := e.G().LoginState().ExternalFunc(f, "loginProvision.device - Run provisionee"); err != nil { return err } // need username, device name for ProvisionUI.ProvisioneeSuccess() e.username = provisionee.GetName() pdevice := provisionee.Device() if pdevice == nil { e.G().Log.Warning("nil provisionee device") } else if pdevice.Description == nil { e.G().Log.Warning("nil provisionee device description") } else { e.devname = *pdevice.Description } return nil }
// Run starts the engine. func (e *DeviceAdd) Run(ctx *Context) (err error) { e.G().Log.Debug("+ DeviceAdd.Run()") defer func() { e.G().Log.Debug("- DeviceAdd.Run() -> %s", libkb.ErrToOk(err)) }() arg := keybase1.ChooseDeviceTypeArg{Kind: keybase1.ChooseType_NEW_DEVICE} provisioneeType, err := ctx.ProvisionUI.ChooseDeviceType(context.TODO(), arg) if err != nil { return err } e.G().Log.Debug("provisionee device type: %v", provisioneeType) // make a new secret: secret, err := libkb.NewKex2Secret() if err != nil { return err } e.G().Log.Debug("secret phrase received") // provisioner needs ppstream, and UI is confusing when it asks for // it at the same time as asking for the secret, so get it first // before prompting for the kex2 secret: pps, err := e.G().LoginState().GetPassphraseStream(ctx.SecretUI) if err != nil { return err } // create provisioner engine provisioner := NewKex2Provisioner(e.G(), secret.Secret(), pps) var canceler func() ctx.NetContext, canceler = context.WithCancel(ctx.NetContext) // display secret and prompt for secret from X in a goroutine: go func() { sb := secret.Secret() arg := keybase1.DisplayAndPromptSecretArg{ Secret: sb[:], Phrase: secret.Phrase(), OtherDeviceType: provisioneeType, } receivedSecret, err := ctx.ProvisionUI.DisplayAndPromptSecret(ctx.NetContext, arg) if err != nil { e.G().Log.Warning("DisplayAndPromptSecret error: %s", err) canceler() } else if receivedSecret.Secret != nil && len(receivedSecret.Secret) > 0 { e.G().Log.Debug("received secret, adding to provisioner") var ks kex2.Secret copy(ks[:], receivedSecret.Secret) provisioner.AddSecret(ks) } else if len(receivedSecret.Phrase) > 0 { e.G().Log.Debug("received secret phrase, adding to provisioner") ks, err := libkb.NewKex2SecretFromPhrase(receivedSecret.Phrase) if err != nil { e.G().Log.Warning("NewKex2SecretFromPhrase error: %s", err) canceler() } else { provisioner.AddSecret(ks.Secret()) } } }() defer func() { canceler() }() if err := RunEngine(provisioner, ctx); err != nil { if err == kex2.ErrHelloTimeout { err = libkb.CanceledError{M: "Failed to provision device: are you sure you typed the secret properly?"} } return err } // provisioning was successful, so the user has changed: e.G().NotifyRouter.HandleKeyfamilyChanged(e.G().Env.GetUID()) // TODO: Remove this after kbfs notification change complete e.G().NotifyRouter.HandleUserChanged(e.G().Env.GetUID()) return nil }