Example #1
0
// 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")
}
Example #2
0
func (r *RemoteLocksmithUI) SelectSigner(ctx context.Context, arg keybase1.SelectSignerArg) (keybase1.SelectSignerRes, error) {
	arg.SessionID = r.sessionID
	return r.uicli.SelectSigner(ctx, arg)
}