github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/revoke.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  type RevokeMode int
    15  
    16  const (
    17  	RevokeKey RevokeMode = iota
    18  	RevokeDevice
    19  )
    20  
    21  type RevokeEngine struct {
    22  	libkb.Contextified
    23  	deviceID             keybase1.DeviceID
    24  	kid                  keybase1.KID
    25  	mode                 RevokeMode
    26  	forceSelf            bool
    27  	forceLast            bool
    28  	skipUserEKForTesting bool // Set for testing
    29  }
    30  
    31  type RevokeDeviceEngineArgs struct {
    32  	ID                   keybase1.DeviceID
    33  	ForceSelf            bool
    34  	ForceLast            bool
    35  	SkipUserEKForTesting bool
    36  }
    37  
    38  func NewRevokeDeviceEngine(g *libkb.GlobalContext, args RevokeDeviceEngineArgs) *RevokeEngine {
    39  	return &RevokeEngine{
    40  		deviceID:             args.ID,
    41  		mode:                 RevokeDevice,
    42  		forceSelf:            args.ForceSelf,
    43  		forceLast:            args.ForceLast,
    44  		skipUserEKForTesting: args.SkipUserEKForTesting,
    45  		Contextified:         libkb.NewContextified(g),
    46  	}
    47  }
    48  
    49  func NewRevokeKeyEngine(g *libkb.GlobalContext, kid keybase1.KID) *RevokeEngine {
    50  	return &RevokeEngine{
    51  		kid:          kid,
    52  		mode:         RevokeKey,
    53  		Contextified: libkb.NewContextified(g),
    54  	}
    55  }
    56  
    57  func (e *RevokeEngine) Name() string {
    58  	return "Revoke"
    59  }
    60  
    61  func (e *RevokeEngine) Prereqs() Prereqs {
    62  	return Prereqs{
    63  		Device: true,
    64  	}
    65  }
    66  
    67  func (e *RevokeEngine) RequiredUIs() []libkb.UIKind {
    68  	return []libkb.UIKind{
    69  		libkb.LogUIKind,
    70  		libkb.SecretUIKind,
    71  	}
    72  }
    73  
    74  func (e *RevokeEngine) SubConsumers() []libkb.UIConsumer {
    75  	return []libkb.UIConsumer{}
    76  }
    77  
    78  func (e *RevokeEngine) getKIDsToRevoke(me *libkb.User) ([]keybase1.KID, error) {
    79  	if e.mode == RevokeDevice {
    80  		deviceKeys, err := me.GetComputedKeyFamily().GetAllActiveKeysForDevice(e.deviceID)
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  		if len(deviceKeys) == 0 {
    85  			return nil, fmt.Errorf("No active keys to revoke for device %s.", e.deviceID)
    86  		}
    87  		return deviceKeys, nil
    88  	} else if e.mode == RevokeKey {
    89  		kid := e.kid
    90  		key, err := me.GetComputedKeyFamily().FindKeyWithKIDUnsafe(kid)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		if !libkb.IsPGP(key) {
    95  			return nil, fmt.Errorf("Key %s is not a PGP key. To revoke device keys, use the `device remove` command.", e.kid)
    96  		}
    97  		for _, activePGPKey := range me.GetComputedKeyFamily().GetActivePGPKeys(false /* sibkeys only */) {
    98  			if activePGPKey.GetKID().Equal(kid) {
    99  				return []keybase1.KID{kid}, nil
   100  			}
   101  		}
   102  		return nil, fmt.Errorf("PGP key %s is not active", e.kid)
   103  	} else {
   104  		return nil, fmt.Errorf("Unknown revoke mode: %d", e.mode)
   105  	}
   106  }
   107  
   108  func (e *RevokeEngine) explicitOrImplicitDeviceID(me *libkb.User) keybase1.DeviceID {
   109  	if e.mode == RevokeDevice {
   110  		return e.deviceID
   111  	}
   112  	for deviceID, device := range me.GetComputedKeyInfos().Devices {
   113  		if device.Kid.Equal(e.kid) {
   114  			return deviceID
   115  		}
   116  	}
   117  	// If we're revoking a PGP key, it won't match any device.
   118  	return ""
   119  }
   120  
   121  func (e *RevokeEngine) Run(mctx libkb.MetaContext) (err error) {
   122  	defer mctx.Trace(fmt.Sprintf("RevokeEngine (mode:%v)", e.mode), &err)()
   123  	return retryOnEphemeralRace(mctx, e.run)
   124  }
   125  
   126  func (e *RevokeEngine) run(m libkb.MetaContext) error {
   127  	e.G().LocalSigchainGuard().Set(m.Ctx(), "RevokeEngine")
   128  	defer e.G().LocalSigchainGuard().Clear(m.Ctx(), "RevokeEngine")
   129  
   130  	me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m))
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	currentDevice := m.G().Env.GetDeviceID()
   136  	var deviceID keybase1.DeviceID
   137  	if e.mode == RevokeDevice {
   138  		deviceID = e.deviceID
   139  		hasPGP := len(me.GetComputedKeyFamily().GetActivePGPKeys(false)) > 0
   140  
   141  		if len(me.GetComputedKeyFamily().GetAllActiveDevices()) == 1 {
   142  			passphraseState, err := libkb.LoadPassphraseState(m)
   143  			if err != nil {
   144  				return fmt.Errorf("could not load passphrase state: %s", err)
   145  			}
   146  			if passphraseState == keybase1.PassphraseState_RANDOM {
   147  				return libkb.RevokeLastDeviceError{NoPassphrase: true}
   148  			}
   149  
   150  			if hasPGP {
   151  				// even w/ forceLast, you cannot revoke your last device
   152  				// if you have a pgp key
   153  				return libkb.RevokeLastDevicePGPError{}
   154  			}
   155  
   156  			if !e.forceLast {
   157  				return libkb.RevokeLastDeviceError{}
   158  			}
   159  		}
   160  
   161  		if e.deviceID == currentDevice && !(e.forceSelf || e.forceLast) {
   162  			return libkb.RevokeCurrentDeviceError{}
   163  		}
   164  
   165  	}
   166  
   167  	kidsToRevoke, err := e.getKIDsToRevoke(me)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	lease, merkleRoot, err := libkb.RequestDowngradeLeaseByKID(m.Ctx(), m.G(), kidsToRevoke)
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	m.UIs().LogUI.Info("Revoking KIDs:")
   178  	for _, kid := range kidsToRevoke {
   179  		m.UIs().LogUI.Info("  %s", kid)
   180  	}
   181  
   182  	sigKey, encKey, err := e.getDeviceSecretKeys(m, me)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	if encKey == nil {
   188  		return errors.New("Missing encryption key")
   189  	}
   190  
   191  	var pukBoxes []keybase1.PerUserKeyBox
   192  	var pukPrev *libkb.PerUserKeyPrev
   193  
   194  	// Whether this run is creating a new per-user-key. Set later.
   195  	addingNewPUK := false
   196  
   197  	// Only used if addingNewPUK
   198  	var newPukGeneration keybase1.PerUserKeyGeneration
   199  	var newPukSeed *libkb.PerUserKeySeed
   200  
   201  	pukring, err := e.G().GetPerUserKeyring(m.Ctx())
   202  	if err != nil {
   203  		return err
   204  	}
   205  	err = pukring.Sync(m)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	if pukring.HasAnyKeys() {
   210  		addingNewPUK = true
   211  		newPukGeneration = pukring.CurrentGeneration() + 1
   212  
   213  		// Make a new per-user-key
   214  		newPukSeedInner, err := libkb.GeneratePerUserKeySeed()
   215  		if err != nil {
   216  			return err
   217  		}
   218  		newPukSeed = &newPukSeedInner
   219  
   220  		// Create a prev secretbox containing the previous generation seed
   221  		pukPrevInner, err := pukring.PreparePrev(m, *newPukSeed, newPukGeneration)
   222  		if err != nil {
   223  			return err
   224  		}
   225  		pukPrev = &pukPrevInner
   226  
   227  		// Get the receivers who will be able to decrypt the new per-user-key
   228  		pukReceivers, err := e.getPukReceivers(m, me, kidsToRevoke)
   229  		if err != nil {
   230  			return err
   231  		}
   232  
   233  		// Create boxes of the new per-user-key
   234  		pukBoxesInner, err := pukring.PrepareBoxesForDevices(m,
   235  			*newPukSeed, newPukGeneration, pukReceivers, encKey)
   236  		if err != nil {
   237  			return err
   238  		}
   239  		pukBoxes = pukBoxesInner
   240  	}
   241  
   242  	var sigsList []libkb.JSONPayload
   243  
   244  	// Push the per-user-key sig
   245  
   246  	// Seqno when the per-user-key will be signed in.
   247  	var newPukSeqno keybase1.Seqno
   248  	if addingNewPUK {
   249  		sig1, err := libkb.PerUserKeyProofReverseSigned(m, me, *newPukSeed, newPukGeneration, sigKey)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		newPukSeqno = me.GetSigChainLastKnownSeqno()
   254  		sigsList = append(sigsList, sig1.Payload)
   255  	}
   256  
   257  	// Push the revoke sig
   258  	sig2, lastSeqno, lastLinkID, err := e.makeRevokeSig(m, me, sigKey, kidsToRevoke, deviceID, merkleRoot)
   259  	if err != nil {
   260  		return err
   261  	}
   262  	sigsList = append(sigsList, sig2)
   263  
   264  	payload := make(libkb.JSONPayload)
   265  	payload["sigs"] = sigsList
   266  	payload["downgrade_lease_id"] = lease.LeaseID
   267  
   268  	if addingNewPUK {
   269  		libkb.AddPerUserKeyServerArg(payload, newPukGeneration, pukBoxes, pukPrev)
   270  	}
   271  
   272  	var myUserEKBox *keybase1.UserEkBoxed
   273  	var newUserEKMetadata *keybase1.UserEkMetadata
   274  	ekLib := e.G().GetEKLib()
   275  	if !e.skipUserEKForTesting && addingNewPUK && ekLib != nil {
   276  		sig, boxes, newMetadata, myBox, err := ekLib.PrepareNewUserEK(m, *merkleRoot, *newPukSeed)
   277  		if err != nil {
   278  			return err
   279  		}
   280  		// The assembled set of boxes includes one for the device we're in the
   281  		// middle of revoking. Filter it out.
   282  		filteredBoxes := []keybase1.UserEkBoxMetadata{}
   283  		deviceIDToFilter := e.explicitOrImplicitDeviceID(me)
   284  		for _, boxMetadata := range boxes {
   285  			if !boxMetadata.RecipientDeviceID.Eq(deviceIDToFilter) {
   286  				filteredBoxes = append(filteredBoxes, boxMetadata)
   287  			}
   288  		}
   289  		// If there are no active deviceEKs, we can't publish this key. This
   290  		// should mostly only come up in tests.
   291  		if len(filteredBoxes) > 0 {
   292  			myUserEKBox = myBox
   293  			newUserEKMetadata = &newMetadata
   294  			userEKSection := make(libkb.JSONPayload)
   295  			userEKSection["sig"] = sig
   296  			userEKSection["boxes"] = filteredBoxes
   297  			payload["user_ek"] = userEKSection
   298  		} else {
   299  			m.Debug("skipping userEK publishing, there are no valid deviceEKs")
   300  		}
   301  	}
   302  	m.Debug("RevokeEngine#Run pukBoxes:%v pukPrev:%v for generation %v, userEKBox: %v",
   303  		len(pukBoxes), pukPrev != nil, newPukGeneration, myUserEKBox != nil)
   304  
   305  	_, err = m.G().API.PostJSON(m, libkb.APIArg{
   306  		Endpoint:    "key/multi",
   307  		SessionType: libkb.APISessionTypeREQUIRED,
   308  		JSONPayload: payload,
   309  	})
   310  	if err != nil {
   311  		// Revoke failed, let's clear downgrade lease so it will not prevent us
   312  		// from trying again for given kids.
   313  		err2 := libkb.CancelDowngradeLease(m.Ctx(), m.G(), lease.LeaseID)
   314  		if err2 != nil {
   315  			m.Warning("Failed to cancel downgrade leases after a failed revoke: %s", err2)
   316  			return libkb.CombineErrors(err, err2)
   317  		}
   318  		return err
   319  	}
   320  	if err = libkb.MerkleCheckPostedUserSig(m, me.GetUID(), lastSeqno, lastLinkID); err != nil {
   321  		return err
   322  	}
   323  
   324  	if addingNewPUK {
   325  		err = pukring.AddKey(m, newPukGeneration, newPukSeqno, *newPukSeed)
   326  		if err != nil {
   327  			return err
   328  		}
   329  	}
   330  
   331  	// Add the new userEK box to local storage, if it was created above.
   332  	if myUserEKBox != nil {
   333  		err = e.G().GetUserEKBoxStorage().Put(m, newUserEKMetadata.Generation, *myUserEKBox)
   334  		if err != nil {
   335  			m.Warning("error while saving userEK box: %s", err)
   336  		}
   337  	}
   338  
   339  	e.G().UserChanged(m.Ctx(), me.GetUID())
   340  
   341  	return nil
   342  }
   343  
   344  // Get the full keys for this device.
   345  // Returns (sigKey, encKey, err)
   346  func (e *RevokeEngine) getDeviceSecretKeys(m libkb.MetaContext, me *libkb.User) (libkb.GenericKey, libkb.GenericKey, error) {
   347  	skaSig := libkb.SecretKeyArg{
   348  		Me:      me,
   349  		KeyType: libkb.DeviceSigningKeyType,
   350  	}
   351  	sigKey, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(skaSig, "to revoke another key"))
   352  	if err != nil {
   353  		return nil, nil, err
   354  	}
   355  	if err = sigKey.CheckSecretKey(); err != nil {
   356  		return nil, nil, err
   357  	}
   358  
   359  	skaEnc := libkb.SecretKeyArg{
   360  		Me:      me,
   361  		KeyType: libkb.DeviceEncryptionKeyType,
   362  	}
   363  	encKey, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(skaEnc, "to revoke another key"))
   364  	if err != nil {
   365  		return nil, nil, err
   366  	}
   367  	if err = encKey.CheckSecretKey(); err != nil {
   368  		return nil, nil, err
   369  	}
   370  
   371  	return sigKey, encKey, err
   372  }
   373  
   374  func (e *RevokeEngine) makeRevokeSig(m libkb.MetaContext, me *libkb.User, sigKey libkb.GenericKey,
   375  	kidsToRevoke []keybase1.KID, deviceID keybase1.DeviceID,
   376  	merkleRoot *libkb.MerkleRoot) (libkb.JSONPayload, keybase1.Seqno, libkb.LinkID, error) {
   377  	proof, err := me.RevokeKeysProof(m, sigKey, kidsToRevoke, deviceID, merkleRoot)
   378  	if err != nil {
   379  		return nil, 0, nil, err
   380  	}
   381  	sig, _, linkID, err := libkb.SignJSON(proof.J, sigKey)
   382  	if err != nil {
   383  		return nil, 0, nil, err
   384  	}
   385  
   386  	sig1 := make(libkb.JSONPayload)
   387  	sig1["sig"] = sig
   388  	sig1["signing_kid"] = sigKey.GetKID().String()
   389  	sig1["type"] = libkb.LinkTypeRevoke
   390  	return sig1, proof.Seqno, linkID, nil
   391  }
   392  
   393  // Get the receivers of the new per-user-key boxes.
   394  // Includes all device subkeys except any being revoked by this engine.
   395  func (e *RevokeEngine) getPukReceivers(m libkb.MetaContext, me *libkb.User, exclude []keybase1.KID) (res []libkb.NaclDHKeyPair, err error) {
   396  	excludeMap := make(map[keybase1.KID]bool)
   397  	for _, kid := range exclude {
   398  		excludeMap[kid] = true
   399  	}
   400  
   401  	ckf := me.GetComputedKeyFamily()
   402  
   403  	for _, dev := range ckf.GetAllActiveDevices() {
   404  		keyGeneric, err := ckf.GetEncryptionSubkeyForDevice(dev.ID)
   405  		if err != nil {
   406  			return nil, err
   407  		}
   408  		if !excludeMap[keyGeneric.GetKID()] {
   409  			key, ok := keyGeneric.(libkb.NaclDHKeyPair)
   410  			if !ok {
   411  				return nil, fmt.Errorf("Unexpected encryption key type: %T", keyGeneric)
   412  			}
   413  			res = append(res, key)
   414  		}
   415  	}
   416  	return res, nil
   417  }