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

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package engine
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/keybase/client/go/libkb"
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  // PassphraseChange engine is used for changing the user's passphrase, either
    15  // by replacement or by force.
    16  type PassphraseChange struct {
    17  	arg        *keybase1.PassphraseChangeArg
    18  	me         *libkb.User
    19  	usingPaper bool
    20  	libkb.Contextified
    21  }
    22  
    23  // NewPassphraseChange creates a new engine for changing user passphrases,
    24  // either if the current passphrase is known, or in "force" mode
    25  func NewPassphraseChange(g *libkb.GlobalContext, a *keybase1.PassphraseChangeArg) *PassphraseChange {
    26  	return &PassphraseChange{
    27  		arg:          a,
    28  		Contextified: libkb.NewContextified(g),
    29  	}
    30  }
    31  
    32  // Name provides the name of the engine for the engine interface
    33  func (c *PassphraseChange) Name() string {
    34  	return "PassphraseChange"
    35  }
    36  
    37  // Prereqs returns engine prereqs
    38  func (c *PassphraseChange) Prereqs() Prereqs {
    39  	if c.arg.Force {
    40  		return Prereqs{}
    41  	}
    42  
    43  	return Prereqs{Device: true}
    44  }
    45  
    46  // RequiredUIs returns the required UIs.
    47  func (c *PassphraseChange) RequiredUIs() []libkb.UIKind {
    48  	return []libkb.UIKind{
    49  		libkb.SecretUIKind,
    50  	}
    51  }
    52  
    53  // SubConsumers requires the other UI consumers of this engine
    54  func (c *PassphraseChange) SubConsumers() []libkb.UIConsumer {
    55  	return []libkb.UIConsumer{
    56  		&PaperKeyGen{},
    57  	}
    58  }
    59  
    60  // Run the engine
    61  func (c *PassphraseChange) Run(m libkb.MetaContext) (err error) {
    62  
    63  	m = m.WithLogTag("PPCHNG")
    64  
    65  	defer m.Trace("PassphraseChange#Run", &err)()
    66  	defer func() {
    67  		m.G().SKBKeyringMu.Unlock()
    68  	}()
    69  	m.G().SKBKeyringMu.Lock()
    70  	m.Debug("| Acquired SKBKeyringMu mutex")
    71  
    72  	if len(c.arg.Passphrase) < libkb.MinPassphraseLength {
    73  		return libkb.PassphraseError{Msg: "too short"}
    74  	}
    75  
    76  	if err = c.loadMe(); err != nil {
    77  		return
    78  	}
    79  
    80  	if _, w := m.ActiveDevice().SyncSecrets(m); w != nil {
    81  		m.Debug("| failed to run secret syncer: %s", w)
    82  	}
    83  
    84  	m = m.WithNewProvisionalLoginContextForUser(c.me)
    85  
    86  	if c.arg.Force {
    87  		err = c.runForcedUpdate(m)
    88  	} else {
    89  		err = c.runStandardUpdate(m)
    90  	}
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	// If the passphrase changes, it's known. Do this in case we aren't getting
    96  	// gregors for some reason (standalone or test) - it will at least update
    97  	// this device.
    98  	libkb.MaybeSavePassphraseState(m, keybase1.PassphraseState_KNOWN)
    99  
   100  	// We used to sync secrets here, but sync secrets in runForceUpdate
   101  	// or runStandardUpdate, since the temporary login information won't
   102  	// persist past the scope of these functions.
   103  	return nil
   104  }
   105  
   106  // findPaperKeys checks if the user has paper keys.  If he/she
   107  // does, it prompts for a paper key phrase.  This is used to
   108  // regenerate paper keys, which are then matched against the
   109  // paper keys found in the keyfamily.
   110  func (c *PassphraseChange) findPaperKeys(m libkb.MetaContext) (*libkb.DeviceWithKeys, error) {
   111  	kp, err := findPaperKeys(m, c.me)
   112  	if err != nil {
   113  		m.Debug("findPaperKeys error: %s", err)
   114  		return nil, err
   115  	}
   116  	m.Debug("findPaperKeys success")
   117  	c.usingPaper = true
   118  	return kp, nil
   119  }
   120  
   121  // findUpdateKeys looks for keys to perform the passphrase update.
   122  // The first choice is device keys.  If that fails, it will look
   123  // for backup keys.  If backup keys are necessary, then it will
   124  // also log the user in with the backup keys.
   125  func (c *PassphraseChange) findUpdateDevice(m libkb.MetaContext) (ad *libkb.ActiveDevice, err error) {
   126  	defer m.Trace("PassphraseChange#findUpdateDevice", &err)()
   127  	if ad = m.G().ActiveDevice; ad.Valid() {
   128  		m.Debug("| returning globally active device key")
   129  		return ad, nil
   130  	}
   131  	kp, err := c.findPaperKeys(m)
   132  	if err != nil {
   133  		m.Debug("| error fetching paper keys")
   134  		return nil, err
   135  	}
   136  	ad = libkb.NewActiveDeviceWithDeviceWithKeys(m, m.CurrentUserVersion(), kp)
   137  	m.Debug("| installing paper key as thread-local active device")
   138  	return ad, nil
   139  }
   140  
   141  func (c *PassphraseChange) forceUpdatePassphrase(m libkb.MetaContext, sigKey libkb.GenericKey, ppGen libkb.PassphraseGeneration, oldClientHalf libkb.LKSecClientHalf) (err error) {
   142  
   143  	defer m.Trace("PassphraseChange#forceUpdatePassphrase", &err)()
   144  
   145  	// Don't update server-synced pgp keys when recovering.
   146  	// This will render any server-synced pgp keys unrecoverable from the server.
   147  	// TODO would it responsible to ask the server to delete them?
   148  	pgpKeys, nPgpKeysLost, err := c.findAndDecryptPrivatePGPKeysLossy(m)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	if nPgpKeysLost > 0 {
   154  		m.Debug("PassphraseChange.runForcedUpdate: Losing %v synced keys", nPgpKeysLost)
   155  	}
   156  
   157  	// Ready the update argument; almost done, but we need some more stuff.
   158  	payload, err := c.commonArgs(m, oldClientHalf, pgpKeys, ppGen)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	// get the new passphrase hash out of the args
   164  	pwh, ok := payload["pwh"].(string)
   165  	if !ok || len(pwh) == 0 {
   166  		return errors.New("no pwh found in common args")
   167  	}
   168  
   169  	// get the new PDPKA5 KID out of the args
   170  	pdpka5kid, ok := payload["pdpka5_kid"].(string)
   171  	if !ok || len(pdpka5kid) == 0 {
   172  		return errors.New("no pdpka5kid found in common args")
   173  	}
   174  
   175  	// Generate a signature with our unlocked sibling key from device.
   176  	proof, err := c.me.UpdatePassphraseProof(m, sigKey, pwh, ppGen+1, pdpka5kid)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	sig, _, _, err := libkb.SignJSON(proof, sigKey)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	payload["sig"] = sig
   186  	payload["signing_kid"] = sigKey.GetKID()
   187  
   188  	postArg := libkb.APIArg{
   189  		Endpoint:    "passphrase/sign",
   190  		SessionType: libkb.APISessionTypeREQUIRED,
   191  		JSONPayload: payload,
   192  	}
   193  
   194  	// Important to pass a MetaContext here to pick up the provisional login context
   195  	// or an ActiveDevice that is thread-local.
   196  	_, err = m.G().API.PostJSON(m, postArg)
   197  	if err != nil {
   198  		return fmt.Errorf("api post to passphrase/sign error: %s", err)
   199  	}
   200  	return nil
   201  }
   202  
   203  // 1. Get keys for decryption and signing
   204  // 2. If necessary, log in with backup keys
   205  // 3. Get lks client half from server
   206  // 4. Post an update passphrase proof
   207  func (c *PassphraseChange) runForcedUpdate(m libkb.MetaContext) (err error) {
   208  	defer m.Trace("PassphraseChange#runForcedUpdate", &err)()
   209  
   210  	ad, err := c.findUpdateDevice(m)
   211  	if err != nil {
   212  		return
   213  	}
   214  	if ad == nil {
   215  		return libkb.NoSecretKeyError{}
   216  	}
   217  
   218  	enc, err := ad.EncryptionKey()
   219  	if err != nil {
   220  		return err
   221  	}
   222  	sig, err := ad.SigningKey()
   223  	if err != nil {
   224  		return err
   225  	}
   226  
   227  	m = m.WithActiveDevice(ad)
   228  	ppGen, oldClientHalf, err := fetchLKS(m, enc)
   229  	if err != nil {
   230  		return
   231  	}
   232  
   233  	err = c.forceUpdatePassphrase(m, sig, ppGen, oldClientHalf)
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	_, err = m.SyncSecrets()
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	m = m.WithGlobalActiveDevice()
   244  	// Reset passphrase stream cache so that subsequent updates go through
   245  	// without a problem (see CORE-3933)
   246  	m.ActiveDevice().ClearCaches()
   247  
   248  	return nil
   249  }
   250  
   251  // runStandardUpdate is for when the user knows the current password.
   252  func (c *PassphraseChange) runStandardUpdate(m libkb.MetaContext) (err error) {
   253  
   254  	defer m.Trace("PassphraseChange.runStandardUpdate", &err)()
   255  
   256  	var ppStream *libkb.PassphraseStream
   257  	if len(c.arg.OldPassphrase) == 0 {
   258  		ppStream, err = libkb.GetPassphraseStreamViaPromptInLoginContext(m)
   259  	} else {
   260  		ppStream, err = libkb.VerifyPassphraseGetStreamInLoginContext(m, c.arg.OldPassphrase)
   261  	}
   262  	if err != nil {
   263  		return err
   264  	}
   265  
   266  	pgpKeys, err := c.findAndDecryptPrivatePGPKeys(m)
   267  	if err != nil {
   268  		return err
   269  	}
   270  
   271  	gen := m.PassphraseStream().Generation()
   272  	oldClientHalf := m.PassphraseStream().LksClientHalf()
   273  
   274  	payload, err := c.commonArgs(m, oldClientHalf, pgpKeys, gen)
   275  	if err != nil {
   276  		return err
   277  	}
   278  
   279  	lp, err := libkb.ComputeLoginPackage2(m, ppStream)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	payload["ppgen"] = gen
   285  	payload["old_pdpka4"] = lp.PDPKA4()
   286  	payload["old_pdpka5"] = lp.PDPKA5()
   287  
   288  	postArg := libkb.APIArg{
   289  		Endpoint:    "passphrase/replace",
   290  		SessionType: libkb.APISessionTypeREQUIRED,
   291  		JSONPayload: payload,
   292  	}
   293  
   294  	_, err = c.G().API.PostJSON(m, postArg)
   295  	if err != nil {
   296  		return err
   297  	}
   298  
   299  	_, err = m.SyncSecrets()
   300  	if err != nil {
   301  		return err
   302  	}
   303  
   304  	// Reset the passphrase stream cache on the global Active Device, since if it exists,
   305  	// it was for a previous version of the passphrase.
   306  	m = m.WithGlobalActiveDevice()
   307  	m.ActiveDevice().ClearCaches()
   308  
   309  	return nil
   310  }
   311  
   312  func (c *PassphraseChange) commonArgs(m libkb.MetaContext, oldClientHalf libkb.LKSecClientHalf, pgpKeys []libkb.GenericKey, existingGen libkb.PassphraseGeneration) (libkb.JSONPayload, error) {
   313  
   314  	salt, err := c.me.GetSalt()
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	tsec, newPPStream, err := libkb.StretchPassphrase(c.G(), c.arg.Passphrase, salt)
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  	newPWH := newPPStream.PWHash()
   324  	newClientHalf := newPPStream.LksClientHalf()
   325  	pdpka5kid, err := newPPStream.PDPKA5KID()
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  
   330  	mask := oldClientHalf.ComputeMask(newClientHalf)
   331  
   332  	lksch := make(map[keybase1.KID]string)
   333  	devices := c.me.GetComputedKeyFamily().GetAllDevices()
   334  	for _, dev := range devices {
   335  		if !dev.IsActive() {
   336  			continue
   337  		}
   338  		key, err := c.me.GetComputedKeyFamily().GetEncryptionSubkeyForDevice(dev.ID)
   339  		if err != nil {
   340  			return nil, err
   341  		}
   342  		ctext, err := key.EncryptToString(newClientHalf.Bytes(), nil)
   343  		if err != nil {
   344  			return nil, err
   345  		}
   346  		lksch[key.GetKID()] = ctext
   347  	}
   348  
   349  	payload := make(libkb.JSONPayload)
   350  	payload["pwh"] = libkb.HexArg(newPWH).String()
   351  	payload["pwh_version"] = libkb.ClientTriplesecVersion
   352  	payload["lks_mask"] = mask.EncodeToHex()
   353  	payload["lks_client_halves"] = lksch
   354  	payload["pdpka5_kid"] = pdpka5kid.String()
   355  
   356  	var encodedKeys []string
   357  	for _, key := range pgpKeys {
   358  		encoded, err := c.encodePrivatePGPKey(key, tsec, existingGen+1)
   359  		if err != nil {
   360  			return nil, err
   361  		}
   362  		encodedKeys = append(encodedKeys, encoded)
   363  	}
   364  	payload["private_keys"] = encodedKeys
   365  
   366  	return payload, nil
   367  }
   368  
   369  func (c *PassphraseChange) loadMe() (err error) {
   370  	c.me, err = libkb.LoadMe(libkb.NewLoadUserForceArg(c.G()))
   371  	return
   372  }
   373  
   374  // findAndDecryptPrivatePGPKeys gets the user's private pgp keys if any exist and decrypts them.
   375  func (c *PassphraseChange) findAndDecryptPrivatePGPKeys(m libkb.MetaContext) ([]libkb.GenericKey, error) {
   376  
   377  	var keyList []libkb.GenericKey
   378  
   379  	// Using a paper key makes TripleSec-synced keys unrecoverable
   380  	if c.usingPaper {
   381  		m.Debug("using a paper key, thus TripleSec-synced keys are unrecoverable")
   382  		return keyList, nil
   383  	}
   384  
   385  	// Only use the synced secret keys:
   386  	blocks, err := c.me.AllSyncedSecretKeys(m)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	secretRetriever := libkb.NewSecretStore(m, c.me.GetNormalizedName())
   392  
   393  	for _, block := range blocks {
   394  		parg := m.SecretKeyPromptArg(libkb.SecretKeyArg{}, "passphrase change")
   395  		key, err := block.PromptAndUnlock(m, parg, secretRetriever, c.me)
   396  		if err != nil {
   397  			return nil, err
   398  		}
   399  		keyList = append(keyList, key)
   400  	}
   401  
   402  	return keyList, nil
   403  }
   404  
   405  // findAndDecryptPrivatePGPKeysLossy gets the user's private pgp keys if any exist and attempts
   406  // to decrypt them without prompting the user. If any fail to decrypt, they are silently not returned.
   407  // The second return value is the number of keys which were not decrypted.
   408  func (c *PassphraseChange) findAndDecryptPrivatePGPKeysLossy(m libkb.MetaContext) ([]libkb.GenericKey, int, error) {
   409  
   410  	var keyList []libkb.GenericKey
   411  	nLost := 0
   412  
   413  	// Only use the synced secret keys:
   414  	blocks, err := c.me.AllSyncedSecretKeys(m)
   415  	if err != nil {
   416  		return nil, 0, err
   417  	}
   418  
   419  	secretRetriever := libkb.NewSecretStore(m, c.me.GetNormalizedName())
   420  
   421  	for _, block := range blocks {
   422  		key, err := block.UnlockNoPrompt(m, secretRetriever)
   423  		if err == nil {
   424  			keyList = append(keyList, key)
   425  		} else {
   426  			if err != libkb.ErrUnlockNotPossible {
   427  				return nil, 0, err
   428  			}
   429  			nLost++
   430  			m.Debug("findAndDecryptPrivatePGPKeysLossy: ignoring failure to decrypt key without prompt")
   431  		}
   432  	}
   433  
   434  	return keyList, nLost, nil
   435  }
   436  
   437  // encodePrivatePGPKey encrypts key with tsec and armor-encodes it.
   438  // It includes the passphrase generation in the data.
   439  func (c *PassphraseChange) encodePrivatePGPKey(key libkb.GenericKey, tsec libkb.Triplesec, gen libkb.PassphraseGeneration) (string, error) {
   440  	skb, err := libkb.ToServerSKB(c.G(), key, tsec, gen)
   441  	if err != nil {
   442  		return "", err
   443  	}
   444  
   445  	return skb.ArmoredEncode()
   446  }