Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions go/engine/login_provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,9 @@ func (e *loginProvision) getValidPaperKeyOnce(m libkb.MetaContext, i int, lastEr
return nil, err
}

// use the KID to find the uid, deviceID and deviceName
var uid keybase1.UID
uid, err = keys.Populate(m)
// Use the KID to find the device in the user we force-reloaded before
// entering provisioning.
err = keys.PopulateFromUser(e.arg.User)
if err != nil {
m.Debug("getValidPaperKeyOnce attempt %d (%s): %s", i, prefix, err)

Expand All @@ -391,10 +391,6 @@ func (e *loginProvision) getValidPaperKeyOnce(m libkb.MetaContext, i int, lastEr
return nil, err
}

if uid.NotEqual(e.arg.User.GetUID()) {
return nil, paperKeyNotFound
}

// found a paper key that can be used for signing
m.Debug("found paper key (%s) match for %s", prefix, e.arg.User.GetName())
return keys, nil
Expand Down Expand Up @@ -872,8 +868,9 @@ func (e *loginProvision) preloadedPaperKey(m libkb.MetaContext, devices []libkb.
return libkb.NoPaperKeysError{}
}

// use the KID to find the uid, deviceID and deviceName
uid, err := keys.Populate(m)
// Resolve the exact KID against the user we force-reloaded before entering
// provisioning. The prefix match above is only a UI convenience.
err = keys.PopulateFromUser(e.arg.User)
if err != nil {
switch err := err.(type) {
case libkb.NotFoundError:
Expand All @@ -885,9 +882,6 @@ func (e *loginProvision) preloadedPaperKey(m libkb.MetaContext, devices []libkb.
}
return err
}
if uid.NotEqual(e.arg.User.GetUID()) {
return paperKeyNotFound
}

return e.paper(m, matchedDevice, keys)
}
Expand Down
4 changes: 4 additions & 0 deletions go/engine/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,10 @@ func TestProvisionPaperCommandLine(t *testing.T) {
err = AssertProvisioned(tc2)
require.NoError(t, err)

deviceCtime, err := tc2.G.ActiveDevice.Ctime(m)
require.NoError(t, err)
require.NotZero(t, deviceCtime)

require.Equal(t, provUI.calledChooseDeviceType, 0)
require.Equal(t, provLoginUI.CalledGetEmailOrUsername, 0)
}
Expand Down
13 changes: 3 additions & 10 deletions go/engine/paperprovision.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package engine

import (
"errors"
"fmt"

"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol/keybase1"
Expand Down Expand Up @@ -97,18 +96,12 @@ func (e *PaperProvisionEngine) Run(m libkb.MetaContext) (err error) {

keys := bkeng.DeviceWithKeys()

// Make sure the key matches the logged in user
// use the KID to find the uid
uid, err := keys.Populate(m)
if err != nil {
// Resolve the exact KID against the user loaded for this provisioning
// attempt.
if err := keys.PopulateFromUser(e.User); err != nil {
return err
}

if uid.NotEqual(e.User.GetUID()) {
e.G().Log.Debug("paper key entered was for a different user")
return fmt.Errorf("paper key valid, but for %s, not %s", uid, e.User.GetUID())
}

e.perUserKeyring, err = libkb.NewPerUserKeyring(e.G(), e.User.GetUID())
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion go/engine/selfprovision.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (e *SelfProvisionEngine) loadUserAndActiveDeviceKeys(m libkb.MetaContext) (
if err != nil {
return nil, err
}
if _, err := keys.Populate(m); err != nil {
if err := keys.PopulateFromUser(e.User); err != nil {
return nil, err
}

Expand Down
60 changes: 24 additions & 36 deletions go/libkb/active_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func NewActiveDeviceWithDeviceWithKeys(m MetaContext, uv keybase1.UserVersion, d
uv: uv,
deviceID: d.deviceID,
deviceName: d.deviceName,
deviceCtime: d.deviceCtime,
signingKey: d.signingKey,
encryptionKey: d.encryptionKey,
nistFactory: NewNISTFactory(m.G(), uv.Uid, d.deviceID, d.signingKey),
Expand Down Expand Up @@ -446,63 +447,50 @@ func (a *ActiveDevice) valid() bool {
}

func (a *ActiveDevice) Ctime(m MetaContext) (keybase1.Time, error) {
// make sure the device id doesn't change throughout this function
deviceID := a.DeviceID()

// check if we have a cached ctime already
ctime, err := a.ctimeCached(deviceID)
if err != nil {
return 0, err
uv, deviceID, ctime := a.ctimeInfo()
if uv.IsNil() || deviceID.IsNil() {
return 0, errors.New("active device is not valid")
}
if ctime > 0 {
return ctime, nil
}

// need to build a device and ask the server for ctimes
decKeys, err := a.deviceKeys(deviceID)
// Resolve the device through the validated UPAK cache. This retries with a
// forced poll if the device is missing, which handles newly provisioned
// devices without relying on the unauthenticated key/owner/device endpoint.
upak, err := m.G().GetUPAKLoader().LoadUPAKWithDeviceID(m.Ctx(), uv.Uid, deviceID)
if err != nil {
return 0, err
}
// Note: decKeys.Populate() makes a network API call
if _, err := decKeys.Populate(m); err != nil {
return 0, nil
if !upak.Current.ToUserVersion().Eq(uv) {
return 0, NewUIDMismatchError("active user changed during ctime lookup")
}
device := upak.Current.FindSigningDeviceKey(deviceID)
if device == nil {
return 0, NoKeyError{"no signing device key found for user"}
}
if device.Base.Revocation != nil {
return 0, NewKeyRevokedError("active device")
}
if device.Base.CTime <= 0 {
return 0, NotFoundError{Msg: "device ctime not found"}
}

// set the ctime value under a write lock
a.Lock()
defer a.Unlock()
if !a.deviceID.Eq(deviceID) {
if !a.uv.Eq(uv) || !a.deviceID.Eq(deviceID) {
return 0, errors.New("active device changed during ctime lookup")
}
a.deviceCtime = decKeys.DeviceCtime()
a.deviceCtime = device.Base.CTime

return a.deviceCtime, nil
}

func (a *ActiveDevice) ctimeCached(deviceID keybase1.DeviceID) (keybase1.Time, error) {
func (a *ActiveDevice) ctimeInfo() (keybase1.UserVersion, keybase1.DeviceID, keybase1.Time) {
a.RLock()
defer a.RUnlock()

if !a.deviceID.Eq(deviceID) {
return 0, errors.New("active device changed during ctime lookup")
}

return a.deviceCtime, nil
}

func (a *ActiveDevice) deviceKeys(deviceID keybase1.DeviceID) (*DeviceWithKeys, error) {
a.RLock()
defer a.RUnlock()

if !a.valid() {
return nil, errors.New("active device is not valid")
}

if !a.deviceID.Eq(deviceID) {
return nil, errors.New("active device changed")
}

return NewDeviceWithKeysOnly(a.signingKey, a.encryptionKey, a.keychainMode), nil
return a.uv, a.deviceID, a.deviceCtime
}

func (a *ActiveDevice) DeviceKeys() (*DeviceWithKeys, error) {
Expand Down
41 changes: 18 additions & 23 deletions go/libkb/device_with_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,32 +111,27 @@ func (s *SelfDestructingDeviceWithKeys) DeviceWithKeys() *DeviceWithKeys {
return &ret
}

type ownerDeviceReply struct {
Status AppStatus `json:"status"`
UID keybase1.UID `json:"uid"`
DeviceID keybase1.DeviceID `json:"device_id"`
DeviceName string `json:"device_name"`
DeviceCtime keybase1.Time `json:"device_ctime"`
}

func (o *ownerDeviceReply) GetAppStatus() *AppStatus {
return &o.Status
}
// PopulateFromUser fills device metadata from an already-loaded and verified
// user sigchain.
func (d *DeviceWithKeys) PopulateFromUser(u *User) error {
if u == nil || d.signingKey == nil {
return NotFoundError{Msg: "KID not found"}
}

func (d *DeviceWithKeys) Populate(m MetaContext) (uid keybase1.UID, err error) {
arg := APIArg{
Endpoint: "key/owner/device",
SessionType: APISessionTypeNONE,
Args: HTTPArgs{"kid": S{Val: d.signingKey.GetKID().String()}},
ckf := u.GetComputedKeyFamily()
if ckf == nil {
return NotFoundError{Msg: "KID not found"}
}
var res ownerDeviceReply
if err = m.G().API.GetDecode(m, arg, &res); err != nil {
return uid, err

device, err := ckf.GetDeviceForKID(d.signingKey.GetKID())
if err != nil || device == nil || !device.IsActive() || device.Description == nil {
return NotFoundError{Msg: "KID not found"}
}
d.deviceID = res.DeviceID
d.deviceName = res.DeviceName
d.deviceCtime = res.DeviceCtime
return res.UID, nil

d.deviceID = device.ID
d.deviceName = *device.Description
d.deviceCtime = device.CTime
return nil
}

func (d *DeviceWithKeys) ToProvisioningKeyActiveDevice(m MetaContext, uv keybase1.UserVersion) *ActiveDevice {
Expand Down