github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/bootstrap_active_device.go (about)

     1  package libkb
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/keybase/client/go/protocol/keybase1"
     7  )
     8  
     9  type LoadUnlockedDeviceKeysMode int
    10  
    11  const (
    12  	// Normal checks the cache and polls if it is stale. StaleOK checks the cache
    13  	// and polls if it is empty. Offline uses the cache and errors if it is empty.
    14  	LoadUnlockedDeviceKeysModeNormal LoadUnlockedDeviceKeysMode = iota
    15  	LoadUnlockedDeviceKeysModeStaleOK
    16  	LoadUnlockedDeviceKeysModeOffline
    17  )
    18  
    19  func loadAndUnlockKey(m MetaContext, kr *SKBKeyringFile, secretStore SecretStore, uid keybase1.UID, kid keybase1.KID) (key GenericKey, err error) {
    20  	defer m.Trace(fmt.Sprintf("loadAndUnlockKey(%s)", kid), &err)()
    21  
    22  	locked := kr.LookupByKid(kid)
    23  	if locked == nil {
    24  		m.Debug("loadAndUnlockKey: no locked key for %s", kid)
    25  		return nil, NoKeyError{fmt.Sprintf("no key for %s", kid)}
    26  	}
    27  	locked.SetUID(uid)
    28  	unlocked, err := locked.UnlockNoPrompt(m, secretStore)
    29  	if err != nil {
    30  		m.Debug("Failed to unlock key %s: %s", kid, err)
    31  		return nil, err
    32  	}
    33  	return unlocked, err
    34  }
    35  
    36  // BootstrapActiveDeviceFromConfig takes the user's config.json, keys.mpack file and
    37  // secret store to populate ActiveDevice, and to have all credentials necessary
    38  // to sign NIST tokens, allowing the user to act as if "logged in". Will return
    39  // nil if everything work, LoginRequiredError if a real "login" is required to
    40  // make the app work, and various errors on unexpected failures.
    41  func BootstrapActiveDeviceFromConfig(m MetaContext, online bool) (uid keybase1.UID, err error) {
    42  	uid, err = bootstrapActiveDeviceFromConfigReturnRawError(m, online, keybase1.UID(""))
    43  	err = fixupBootstrapError(err)
    44  	return uid, err
    45  }
    46  
    47  func bootstrapActiveDeviceFromConfigReturnRawError(m MetaContext, online bool, assertUID keybase1.UID) (uid keybase1.UID, err error) {
    48  	uid = m.G().Env.GetUID()
    49  	if uid.IsNil() {
    50  		return uid, NoUIDError{}
    51  	}
    52  	if assertUID.Exists() && !assertUID.Equal(uid) {
    53  		return uid, NewUIDMismatchError(fmt.Sprintf("wanted %s but got %s", assertUID, uid))
    54  	}
    55  	deviceID := m.G().Env.GetDeviceIDForUID(uid)
    56  	if deviceID.IsNil() {
    57  		return uid, NoDeviceError{fmt.Sprintf("no device in config for UID=%s", uid)}
    58  	}
    59  	err = bootstrapActiveDeviceReturnRawError(m, uid, deviceID, online)
    60  	return uid, err
    61  }
    62  
    63  func isBootstrapLoggedOutError(err error) bool {
    64  	if _, ok := err.(NoUIDError); ok {
    65  		return true
    66  	}
    67  	if err == ErrUnlockNotPossible {
    68  		return true
    69  	}
    70  	return false
    71  }
    72  
    73  func fixupBootstrapError(err error) error {
    74  	if err == nil {
    75  		return nil
    76  	}
    77  	if isBootstrapLoggedOutError(err) {
    78  		return LoginRequiredError{err.Error()}
    79  	}
    80  	return err
    81  }
    82  
    83  func bootstrapActiveDeviceReturnRawError(m MetaContext, uid keybase1.UID, deviceID keybase1.DeviceID, online bool) (err error) {
    84  	defer m.Trace(fmt.Sprintf("bootstrapActiveDeviceReturnRaw(%s,%s)", uid, deviceID), &err)()
    85  
    86  	ad := m.ActiveDevice()
    87  	if ad.IsValidFor(uid, deviceID) {
    88  		m.Debug("active device is current")
    89  		return nil
    90  	}
    91  	mode := LoadUnlockedDeviceKeysModeNormal
    92  	if !online {
    93  		mode = LoadUnlockedDeviceKeysModeOffline
    94  	}
    95  	uv, sib, sub, deviceName, err := LoadUnlockedDeviceKeys(m, uid, deviceID, mode)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	err = m.SetActiveDevice(uv, deviceID, sib, sub, deviceName, KeychainModeOS)
   100  	return err
   101  }
   102  
   103  func LoadUnlockedDeviceKeys(m MetaContext, uid keybase1.UID, deviceID keybase1.DeviceID, mode LoadUnlockedDeviceKeysMode) (uv keybase1.UserVersion, sib GenericKey, sub GenericKey, deviceName string, err error) {
   104  	defer m.Trace(fmt.Sprintf("LoadUnlockedDeviceKeys(uid=%q)", uid), &err)()
   105  
   106  	// use the UPAKLoader with StaleOK, CachedOnly in order to get cached upak
   107  	arg := NewLoadUserArgWithMetaContext(m).WithUID(uid).WithPublicKeyOptional()
   108  
   109  	if mode == LoadUnlockedDeviceKeysModeOffline {
   110  		arg = arg.WithStaleOK(true).WithCachedOnly(true)
   111  	} else if mode == LoadUnlockedDeviceKeysModeStaleOK {
   112  		arg = arg.WithStaleOK(true)
   113  	}
   114  	upak, _, err := m.G().GetUPAKLoader().LoadV2(arg)
   115  	if err != nil {
   116  		m.Debug("BootstrapActiveDevice: upak.Load err: %s", err)
   117  		return uv, nil, nil, deviceName, err
   118  	}
   119  
   120  	if upak.Current.Status == keybase1.StatusCode_SCDeleted {
   121  		m.Debug("User %s was deleted", uid)
   122  		return uv, nil, nil, deviceName, UserDeletedError{}
   123  	}
   124  
   125  	device := upak.Current.FindSigningDeviceKey(deviceID)
   126  	if device == nil {
   127  		m.Debug("BootstrapActiveDevice: no sibkey found for device %s", deviceID)
   128  		return uv, nil, nil, deviceName, NoKeyError{"no signing device key found for user"}
   129  	}
   130  
   131  	if device.Base.Revocation != nil {
   132  		m.Debug("BootstrapActiveDevice: device %s was revoked", deviceID)
   133  		return uv, nil, nil, deviceName, NewKeyRevokedError("active device")
   134  	}
   135  
   136  	sibkeyKID := device.Base.Kid
   137  	deviceName = device.DeviceDescription
   138  
   139  	subkeyKID := upak.Current.FindEncryptionKIDFromSigningKID(sibkeyKID)
   140  	if subkeyKID.IsNil() {
   141  		m.Debug("BootstrapActiveDevice: no subkey found for device: %s", deviceID)
   142  		return uv, nil, nil, deviceName, NoKeyError{"no encryption device key found for user"}
   143  	}
   144  
   145  	// load the keyring file
   146  	username := NewNormalizedUsername(upak.Current.Username)
   147  	kr, err := LoadSKBKeyring(m, username)
   148  	if err != nil {
   149  		m.Debug("BootstrapActiveDevice: error loading keyring for %s: %s", username, err)
   150  		return uv, nil, nil, deviceName, err
   151  	}
   152  
   153  	secretStore := NewSecretStore(m, username)
   154  	sib, err = loadAndUnlockKey(m, kr, secretStore, uid, sibkeyKID)
   155  	if err != nil {
   156  		return uv, nil, nil, deviceName, err
   157  	}
   158  	sub, err = loadAndUnlockKey(m, kr, secretStore, uid, subkeyKID)
   159  	if err != nil {
   160  		return uv, nil, nil, deviceName, err
   161  	}
   162  
   163  	return upak.Current.ToUserVersion(), sib, sub, deviceName, nil
   164  }
   165  
   166  func LoadProvisionalActiveDevice(m MetaContext, uid keybase1.UID, deviceID keybase1.DeviceID, online bool) (ret *ActiveDevice, err error) {
   167  	defer m.Trace("LoadProvisionalActiveDevice", &err)()
   168  	mode := LoadUnlockedDeviceKeysModeStaleOK
   169  	if !online {
   170  		mode = LoadUnlockedDeviceKeysModeOffline
   171  	}
   172  	uv, sib, sub, deviceName, err := LoadUnlockedDeviceKeys(m, uid, deviceID, mode)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	ret = NewProvisionalActiveDevice(m, uv, deviceID, sib, sub, deviceName, KeychainModeOS)
   177  	return ret, nil
   178  }
   179  
   180  // BootstrapActiveDeviceWithMetaContext will setup an ActiveDevice with a NIST Factory
   181  // for the caller. The m.loginContext passed through isn't really needed
   182  // for anything aside from assertions, but as we phase out LoginState, we'll
   183  // leave it here so that assertions in LoginState can still pass.
   184  func BootstrapActiveDeviceWithMetaContext(m MetaContext) (ok bool, uid keybase1.UID, err error) {
   185  	uid, err = BootstrapActiveDeviceFromConfig(m, true)
   186  	ok = false
   187  	if err == nil {
   188  		ok = true
   189  	} else if _, isLRE := err.(LoginRequiredError); isLRE {
   190  		err = nil
   191  	}
   192  	return ok, uid, err
   193  }
   194  
   195  // BootstrapActiveDeviceWithMetaContextAndAssertUID will setup an ActiveDevice with a NIST Factory
   196  // for the caller. It only works if we're logged in as the given UID
   197  func BootstrapActiveDeviceWithMetaContextAndAssertUID(m MetaContext, uid keybase1.UID) (ok bool, err error) {
   198  	_, err = bootstrapActiveDeviceFromConfigReturnRawError(m, true, uid)
   199  	switch err.(type) {
   200  	case nil:
   201  		return true, nil
   202  	case LoginRequiredError:
   203  		return false, nil
   204  	case UIDMismatchError:
   205  		return false, nil
   206  	case NoUIDError:
   207  		return false, nil
   208  	default:
   209  		if err == ErrUnlockNotPossible {
   210  			return false, nil
   211  		}
   212  		return false, err
   213  	}
   214  }