github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/provision_utils.go (about)

     1  package engine
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/keybase/client/go/libkb"
     8  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
     9  )
    10  
    11  func retryOnEphemeralRace(mctx libkb.MetaContext, fn func(mctx libkb.MetaContext) error) (err error) {
    12  	for attempt := 0; attempt < 5; attempt++ {
    13  		if err = fn(mctx); err == nil {
    14  			return nil
    15  		}
    16  		if !libkb.IsEphemeralRetryableError(err) {
    17  			return err
    18  		}
    19  		mctx.Debug("retryOnEphemeralRace found a retryable error on try %d: %v",
    20  			attempt, err)
    21  	}
    22  	return err
    23  }
    24  
    25  // ephemeralKeyReboxer will rebox the lastest userEK while provisioning
    26  // devices.  The provisionee generates a deviceEK seed so that the provisioner
    27  // can rebox the latest userEK for the new deviceKID.  The provisionee posts
    28  // the userEKBox and deviceEKStatement when posting the new device keys.  Once
    29  // fully provisioned the provisionee saves the new deviceEK to storage,
    30  // encrypted by the newly created device.
    31  type ephemeralKeyReboxer struct {
    32  	deviceEKSeed      keybase1.Bytes32
    33  	seedGenerated     bool
    34  	deviceEKStatement keybase1.DeviceEkStatement
    35  	userEKBox         *keybase1.UserEkBoxed
    36  }
    37  
    38  func newEphemeralKeyReboxer() *ephemeralKeyReboxer {
    39  	return &ephemeralKeyReboxer{}
    40  }
    41  
    42  func (e *ephemeralKeyReboxer) getDeviceEKSeed(mctx libkb.MetaContext) (err error) {
    43  	if ekLib := mctx.G().GetEKLib(); ekLib != nil && !e.seedGenerated {
    44  		e.deviceEKSeed, err = ekLib.NewEphemeralSeed()
    45  		if err != nil {
    46  			return err
    47  		}
    48  		e.seedGenerated = true
    49  	}
    50  	return nil
    51  }
    52  
    53  func (e *ephemeralKeyReboxer) getDeviceEKKID(mctx libkb.MetaContext) (kid keybase1.KID, err error) {
    54  	if !e.seedGenerated {
    55  		if err := e.getDeviceEKSeed(mctx); err != nil {
    56  			return "", err
    57  		}
    58  	}
    59  	if ekLib := mctx.G().GetEKLib(); ekLib != nil {
    60  		ekPair := ekLib.DeriveDeviceDHKey(e.deviceEKSeed)
    61  		return ekPair.GetKID(), nil
    62  	}
    63  	return "", nil
    64  }
    65  
    66  func (e *ephemeralKeyReboxer) getReboxArg(mctx libkb.MetaContext, userEKBox *keybase1.UserEkBoxed,
    67  	deviceID keybase1.DeviceID, signingKey libkb.GenericKey) (userEKReboxArg *keybase1.UserEkReboxArg, err error) {
    68  	defer mctx.Trace("ephemeralKeyReboxer#getReboxArg", &err)()
    69  
    70  	ekLib := mctx.G().GetEKLib()
    71  	if ekLib == nil {
    72  		return nil, nil
    73  	}
    74  
    75  	if userEKBox == nil { // We will create EKs after provisioning in the normal way
    76  		mctx.Debug("userEKBox nil, no ephemeral keys created during provisioning")
    77  		return nil, nil
    78  	}
    79  
    80  	deviceEKStatement, deviceEKStatementSig, err := ekLib.SignedDeviceEKStatementFromSeed(
    81  		mctx, userEKBox.DeviceEkGeneration, e.deviceEKSeed, signingKey)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	userEKReboxArg = &keybase1.UserEkReboxArg{
    87  		UserEkBoxMetadata: keybase1.UserEkBoxMetadata{
    88  			Box:                 userEKBox.Box,
    89  			RecipientDeviceID:   deviceID,
    90  			RecipientGeneration: userEKBox.DeviceEkGeneration,
    91  		},
    92  		DeviceID:             deviceID,
    93  		DeviceEkStatementSig: deviceEKStatementSig,
    94  	}
    95  
    96  	e.deviceEKStatement = deviceEKStatement
    97  	e.userEKBox = userEKBox
    98  
    99  	return userEKReboxArg, nil
   100  }
   101  
   102  func (e *ephemeralKeyReboxer) storeEKs(mctx libkb.MetaContext) (err error) {
   103  	defer mctx.Trace("ephemeralKeyReboxer#storeEKs", &err)()
   104  	if ekLib := mctx.G().GetEKLib(); ekLib == nil {
   105  		return nil
   106  	}
   107  	if e.userEKBox == nil {
   108  		mctx.Debug("userEKBox nil, no ephemeral keys to store")
   109  		return nil
   110  	}
   111  
   112  	if !e.seedGenerated {
   113  		return fmt.Errorf("Unable to store EKs with out generating a seed first")
   114  	}
   115  
   116  	deviceEKStorage := mctx.G().GetDeviceEKStorage()
   117  	metadata := e.deviceEKStatement.CurrentDeviceEkMetadata
   118  	if err = deviceEKStorage.Put(mctx, metadata.Generation, keybase1.DeviceEk{
   119  		Seed:     e.deviceEKSeed,
   120  		Metadata: metadata,
   121  	}); err != nil {
   122  		return err
   123  	}
   124  
   125  	userEKBoxStorage := mctx.G().GetUserEKBoxStorage()
   126  	return userEKBoxStorage.Put(mctx, e.userEKBox.Metadata.Generation, *e.userEKBox)
   127  }
   128  
   129  func makeUserEKBoxForProvisionee(mctx libkb.MetaContext, kid keybase1.KID) (*keybase1.UserEkBoxed, error) {
   130  	ekLib := mctx.G().GetEKLib()
   131  	if ekLib == nil {
   132  		return nil, nil
   133  	}
   134  	ekPair, err := libkb.ImportKeypairFromKID(kid)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	receiverKey, ok := ekPair.(libkb.NaclDHKeyPair)
   139  	if !ok {
   140  		return nil, fmt.Errorf("Unexpected receiver key type")
   141  	}
   142  	// This is hardcoded to 1 since we're provisioning a new device.
   143  	deviceEKGeneration := keybase1.EkGeneration(1)
   144  	return ekLib.BoxLatestUserEK(mctx, receiverKey, deviceEKGeneration)
   145  }
   146  
   147  func verifyLocalStorage(m libkb.MetaContext, username string, uid keybase1.UID) {
   148  	m.Debug("verifying local storage")
   149  	defer m.Debug("done verifying local storage")
   150  	normUsername := libkb.NewNormalizedUsername(username)
   151  
   152  	// check config.json looks ok
   153  	verifyRegularFile(m, "config", m.G().Env.GetConfigFilename())
   154  	cr := m.G().Env.GetConfig()
   155  	if cr.GetUsername() != normUsername {
   156  		m.Debug("config username %q doesn't match engine username %q", cr.GetUsername(), normUsername)
   157  	}
   158  	if cr.GetUID().NotEqual(uid) {
   159  		m.Debug("config uid %q doesn't match engine uid %q", cr.GetUID(), uid)
   160  	}
   161  
   162  	// check keys in secretkeys.mpack
   163  	verifyRegularFile(m, "secretkeys", m.G().SKBFilenameForUser(normUsername))
   164  
   165  	// check secret stored
   166  	secret, err := m.G().SecretStore().RetrieveSecret(m, normUsername)
   167  	if err != nil {
   168  		m.Debug("failed to retrieve secret for %s: %s", username, err)
   169  	}
   170  	if secret.IsNil() || len(secret.Bytes()) == 0 {
   171  		m.Debug("retrieved nil/empty secret for %s", username)
   172  	}
   173  }
   174  
   175  func verifyRegularFile(m libkb.MetaContext, name, filename string) {
   176  	info, err := os.Stat(filename)
   177  	if err != nil {
   178  		m.Debug("stat %s file %q error: %s", name, filename, err)
   179  		return
   180  	}
   181  
   182  	m.Debug("%s file %q size: %d", name, filename, info.Size())
   183  	if !info.Mode().IsRegular() {
   184  		m.Debug("%s file %q not regular: %s", name, filename, info.Mode())
   185  	}
   186  }