// deviceSign is used to sign a new installation of keybase on a // new device. It happens when the user has keys already, either // a device key, pgp key, or both. func (d *Locksmith) deviceSign(ctx *Context, withPGPOption bool) error { newDeviceName, err := d.deviceName(ctx) if err != nil { return err } devFilter := libkb.DeviceTypeSet{ libkb.DeviceTypeDesktop: true, libkb.DeviceTypeMobile: true, libkb.DeviceTypePaper: true, } var devs libkb.DeviceKeyMap if ctx.LoginContext != nil { devs, err = ctx.LoginContext.SecretSyncer().ActiveDevices(devFilter) } else { aerr := d.G().LoginState().SecretSyncer(func(ss *libkb.SecretSyncer) { devs, err = ss.ActiveDevices(devFilter) }, "Locksmith - deviceSign - ActiveDevices") if aerr != nil { return aerr } } if err != nil { return err } if len(devs) == 0 && withPGPOption { if d.hasSingleSyncedPGPKey(ctx) { // the user only has a synced pgp key, so bypass the // select signer interface. return d.deviceSignPGP(ctx) } } var arg keybase1.SelectSignerArg for k, v := range devs { if v.Type != libkb.DeviceTypePaper { arg.Devices = append(arg.Devices, keybase1.Device{Type: v.Type, Name: v.Description, DeviceID: k}) } else { arg.HasPaperBackupKey = true } } arg.HasPGP = withPGPOption totalTries := 10 for i := 0; i < totalTries; i++ { if i > 0 { ctx.LogUI.Debug("retrying device sign process") } resCh := make(chan keybase1.SelectSignerRes) errCh := make(chan error) go func() { res, err := ctx.LocksmithUI.SelectSigner(context.TODO(), arg) if err != nil { errCh <- err } else { resCh <- res } }() var res keybase1.SelectSignerRes select { case res = <-resCh: case e := <-errCh: return e case <-d.canceled: return libkb.CanceledError{M: "locksmith canceled while prompting user for signer"} } if res.Action == keybase1.SelectSignerAction_CANCEL { return libkb.CanceledError{M: "cancel requested by user"} } if res.Action != keybase1.SelectSignerAction_SIGN { return fmt.Errorf("unknown action value: %d", res.Action) } // sign action: switch res.Signer.Kind { case keybase1.DeviceSignerKind_PGP: err := d.deviceSignPGP(ctx) if err == nil { ctx.LogUI.Debug("device sign w/ pgp success") return nil } // If there are no available public keys, and no other ways to login, // then no point in continuing. Bail out. if _, ok := err.(libkb.NoKeyError); ok && !arg.HasPaperBackupKey && len(arg.Devices) == 0 { return err } ctx.LogUI.Info("PGP: %s", err) uiarg := keybase1.DeviceSignAttemptErrArg{ Msg: err.Error(), Attempt: i + 1, Total: totalTries, } if err = ctx.LocksmithUI.DeviceSignAttemptErr(context.TODO(), uiarg); err != nil { d.G().Log.Info("error making ui call DeviceSignAttemptErr: %s", err) } case keybase1.DeviceSignerKind_DEVICE: if res.Signer.DeviceID == nil { return fmt.Errorf("selected device for signing, but DeviceID is nil") } if res.Signer.DeviceName == nil { return fmt.Errorf("selected device for signing, but DeviceName is nil") } err := d.deviceSignExistingDevice(ctx, *res.Signer.DeviceID, *res.Signer.DeviceName, newDeviceName, libkb.DeviceTypeDesktop) if err == nil { ctx.LogUI.Debug("device sign w/ existing device success") return nil } ctx.LogUI.Info("deviceSignExistingDevice error: %s", err) if err == kex.ErrProtocolEOF { ctx.LogUI.Info("deviceSignExistingDevice not retrying after EOF error") return libkb.CanceledError{} } if _, ok := err.(libkb.AppStatusError); ok { ctx.LogUI.Info("deviceSignExistingDevice not retrying after AppStatusError: %s", err) return err } uiarg := keybase1.DeviceSignAttemptErrArg{ Msg: err.Error(), Attempt: i + 1, Total: totalTries, } if err = ctx.LocksmithUI.DeviceSignAttemptErr(context.TODO(), uiarg); err != nil { d.G().Log.Info("error making ui call DeviceSignAttemptErr: %s", err) } case keybase1.DeviceSignerKind_PAPER_BACKUP_KEY: err := d.deviceSignPaper(ctx) if err == nil { ctx.LogUI.Debug("device sign w/ paper backup key success") return nil } ctx.LogUI.Errorf("deviceSignPaper error: %s", err) uiarg := keybase1.DeviceSignAttemptErrArg{ Msg: err.Error(), Attempt: i + 1, Total: totalTries, } if err = ctx.LocksmithUI.DeviceSignAttemptErr(context.TODO(), uiarg); err != nil { d.G().Log.Info("error making ui call DeviceSignAttemptErr: %s", err) } default: return fmt.Errorf("unknown signer kind: %d", res.Signer.Kind) } } return fmt.Errorf("device sign process retry attempts exhausted") }
func (r *RemoteLocksmithUI) SelectSigner(ctx context.Context, arg keybase1.SelectSignerArg) (keybase1.SelectSignerRes, error) { arg.SessionID = r.sessionID return r.uicli.SelectSigner(ctx, arg) }