github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/paperkey_gen.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  	"bytes"
     8  	"errors"
     9  	"fmt"
    10  
    11  	"github.com/keybase/go-crypto/ed25519"
    12  	"golang.org/x/crypto/nacl/box"
    13  	"golang.org/x/crypto/scrypt"
    14  
    15  	"github.com/keybase/client/go/kbcrypto"
    16  	"github.com/keybase/client/go/libkb"
    17  	"github.com/keybase/client/go/protocol/keybase1"
    18  )
    19  
    20  type PaperKeyGenArg struct {
    21  	Passphrase libkb.PaperKeyPhrase
    22  	SkipPush   bool
    23  
    24  	// One of Me or UID is required
    25  	// Me is required if not SkipPush
    26  	Me  *libkb.User
    27  	UID keybase1.UID
    28  
    29  	SigningKey     libkb.GenericKey      // optional
    30  	EncryptionKey  libkb.NaclDHKeyPair   // optional
    31  	PerUserKeyring *libkb.PerUserKeyring // optional
    32  	IsEldest       bool
    33  }
    34  
    35  // PaperKeyGen is an engine.
    36  type PaperKeyGen struct {
    37  	arg *PaperKeyGenArg
    38  
    39  	// keys of the generated paper key
    40  	sigKey   libkb.GenericKey
    41  	encKey   libkb.NaclDHKeyPair
    42  	deviceID keybase1.DeviceID
    43  
    44  	libkb.Contextified
    45  }
    46  
    47  // NewPaperKeyGen creates a PaperKeyGen engine.
    48  func NewPaperKeyGen(g *libkb.GlobalContext, arg *PaperKeyGenArg) *PaperKeyGen {
    49  	return &PaperKeyGen{
    50  		arg:          arg,
    51  		Contextified: libkb.NewContextified(g),
    52  	}
    53  }
    54  
    55  // Name is the unique engine name.
    56  func (e *PaperKeyGen) Name() string {
    57  	return "PaperKeyGen"
    58  }
    59  
    60  // GetPrereqs returns the engine prereqs.
    61  func (e *PaperKeyGen) Prereqs() Prereqs {
    62  	// only need a device if pushing keys
    63  	return Prereqs{
    64  		Device: !e.arg.SkipPush && !e.arg.IsEldest,
    65  	}
    66  }
    67  
    68  // RequiredUIs returns the required UIs.
    69  func (e *PaperKeyGen) RequiredUIs() []libkb.UIKind {
    70  	return []libkb.UIKind{}
    71  }
    72  
    73  // SubConsumers returns the other UI consumers for this engine.
    74  func (e *PaperKeyGen) SubConsumers() []libkb.UIConsumer {
    75  	return nil
    76  }
    77  
    78  func (e *PaperKeyGen) SigKey() libkb.GenericKey {
    79  	return e.sigKey
    80  }
    81  
    82  func (e *PaperKeyGen) EncKey() libkb.NaclDHKeyPair {
    83  	return e.encKey
    84  }
    85  
    86  func (e *PaperKeyGen) DeviceID() keybase1.DeviceID {
    87  	return e.deviceID
    88  }
    89  
    90  func (e *PaperKeyGen) DeviceWithKeys() *libkb.DeviceWithKeys {
    91  	return libkb.NewDeviceWithKeysOnly(e.sigKey, e.encKey, libkb.KeychainModeNone)
    92  }
    93  
    94  // Run starts the engine.
    95  func (e *PaperKeyGen) Run(m libkb.MetaContext) error {
    96  	if !e.arg.SkipPush {
    97  		err := e.syncPUK(m)
    98  		if err != nil {
    99  			return err
   100  		}
   101  	}
   102  
   103  	// make the passphrase stream
   104  	key, err := scrypt.Key(e.arg.Passphrase.Bytes(), nil,
   105  		libkb.PaperKeyScryptCost, libkb.PaperKeyScryptR, libkb.PaperKeyScryptP, libkb.PaperKeyScryptKeylen)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	ppStream := libkb.NewPassphraseStream(key)
   111  
   112  	// make keys for the paper device
   113  	if err := e.makeSigKey(ppStream.EdDSASeed()); err != nil {
   114  		return err
   115  	}
   116  	if err := e.makeEncKey(ppStream.DHSeed()); err != nil {
   117  		return err
   118  	}
   119  
   120  	// push everything to the server
   121  	if err := e.push(m); err != nil {
   122  		return err
   123  	}
   124  
   125  	// no need to notify if key wasn't pushed to server
   126  	// (e.g. in the case of using this engine to verify a key)
   127  	if e.arg.SkipPush {
   128  		return nil
   129  	}
   130  
   131  	e.G().KeyfamilyChanged(m.Ctx(), e.getUID())
   132  
   133  	return nil
   134  }
   135  
   136  func (e *PaperKeyGen) getUID() keybase1.UID {
   137  	if !e.arg.UID.IsNil() {
   138  		return e.arg.UID
   139  	}
   140  	if e.arg.Me != nil {
   141  		return e.arg.Me.GetUID()
   142  	}
   143  	return keybase1.UID("")
   144  }
   145  
   146  func (e *PaperKeyGen) syncPUK(m libkb.MetaContext) error {
   147  	// Sync the per-user-key keyring before updating other things.
   148  	pukring, err := e.getPerUserKeyring(m)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	var upak *keybase1.UserPlusAllKeys
   153  	if e.arg.Me != nil {
   154  		tmp := e.arg.Me.ExportToUserPlusAllKeys()
   155  		upak = &tmp
   156  	}
   157  	err = pukring.SyncWithExtras(m, upak)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	return nil
   162  }
   163  
   164  func (e *PaperKeyGen) makeSigKey(seed []byte) error {
   165  	pub, priv, err := ed25519.GenerateKey(bytes.NewBuffer(seed))
   166  	if err != nil {
   167  		return err
   168  	}
   169  
   170  	var key libkb.NaclSigningKeyPair
   171  	copy(key.Public[:], pub[:])
   172  	key.Private = &kbcrypto.NaclSigningKeyPrivate{}
   173  	copy(key.Private[:], priv[:])
   174  
   175  	e.sigKey = key
   176  
   177  	return nil
   178  }
   179  
   180  func (e *PaperKeyGen) makeEncKey(seed []byte) error {
   181  	pub, priv, err := box.GenerateKey(bytes.NewBuffer(seed))
   182  	if err != nil {
   183  		return err
   184  	}
   185  	var key libkb.NaclDHKeyPair
   186  	copy(key.Public[:], (*pub)[:])
   187  	key.Private = &libkb.NaclDHKeyPrivate{}
   188  	copy(key.Private[:], (*priv)[:])
   189  
   190  	e.encKey = key
   191  
   192  	return nil
   193  }
   194  
   195  func (e *PaperKeyGen) getClientHalfFromSecretStore(m libkb.MetaContext) (clientHalf libkb.LKSecClientHalf, ppgen libkb.PassphraseGeneration, err error) {
   196  	defer m.Trace("PaperKeyGen#getClientHalfFromSecretStore", &err)
   197  
   198  	secretStore := libkb.NewSecretStore(m, e.arg.Me.GetNormalizedName())
   199  	if secretStore == nil {
   200  		return clientHalf, ppgen, errors.New("No secret store available")
   201  	}
   202  
   203  	secret, err := secretStore.RetrieveSecret(m)
   204  	if err != nil {
   205  		return clientHalf, ppgen, err
   206  	}
   207  
   208  	devid := e.G().Env.GetDeviceID()
   209  	if devid.IsNil() {
   210  		return clientHalf, ppgen, fmt.Errorf("no device id set")
   211  	}
   212  	var ss *libkb.SecretSyncer
   213  	ss, err = m.ActiveDevice().SyncSecrets(m)
   214  	if err != nil {
   215  		return clientHalf, ppgen, err
   216  	}
   217  	var dev libkb.DeviceKey
   218  	dev, err = ss.FindDevice(devid)
   219  	if err != nil {
   220  		return clientHalf, ppgen, err
   221  	}
   222  	serverHalf, err := libkb.NewLKSecServerHalfFromHex(dev.LksServerHalf)
   223  	if err != nil {
   224  		return clientHalf, ppgen, err
   225  	}
   226  
   227  	clientHalf = serverHalf.ComputeClientHalf(secret)
   228  
   229  	return clientHalf, dev.PPGen, nil
   230  }
   231  
   232  func (e *PaperKeyGen) push(m libkb.MetaContext) (err error) {
   233  	defer m.Trace("PaperKeyGen#push", &err)()
   234  	if e.arg.SkipPush {
   235  		return nil
   236  	}
   237  
   238  	if e.arg.Me == nil {
   239  		return errors.New("no Me object but we wanted to push public keys")
   240  	}
   241  
   242  	// Create a new paper key device. Need the passphrase prefix
   243  	// for the paper device name.  This is the first two words in
   244  	// the passphrase.  There is sufficient entropy to cover this...
   245  	backupDev, err := libkb.NewPaperDevice(e.arg.Passphrase.Prefix())
   246  	if err != nil {
   247  		return err
   248  	}
   249  	e.deviceID = backupDev.ID
   250  
   251  	// create lks halves for this device.  Note that they aren't used for
   252  	// local, encrypted storage of the paper keys, but just for recovery
   253  	// purposes.
   254  	m.Dump()
   255  
   256  	// Clear the passphrase stream if it's outdated, just in case some
   257  	// other device changed the passphrase.
   258  	if err := m.G().ActiveDevice.ClearPassphraseStreamCacheIfOutdated(m); err != nil {
   259  		return err
   260  	}
   261  
   262  	var ppgen libkb.PassphraseGeneration
   263  	var clientHalf libkb.LKSecClientHalf
   264  	if stream := m.PassphraseStream(); stream != nil {
   265  		m.Debug("Got cached passphrase stream")
   266  		clientHalf = stream.LksClientHalf()
   267  		ppgen = stream.Generation()
   268  	} else {
   269  		m.Debug("Got nil passphrase stream; going to secret store")
   270  		// stream was nil, so we must have loaded lks from the secret
   271  		// store.
   272  		clientHalf, ppgen, err = e.getClientHalfFromSecretStore(m)
   273  		switch err.(type) {
   274  		case nil:
   275  		case libkb.SecretStoreError:
   276  			// as a last resort try to prompt the user for their passphrase if
   277  			// the SecretUI is present
   278  			if m.UIs().HasUI(libkb.SecretUIKind) {
   279  				if pps, _, perr := libkb.GetPassphraseStreamViaPrompt(m); pps != nil {
   280  					clientHalf = pps.LksClientHalf()
   281  					ppgen = pps.Generation()
   282  				} else if perr != nil {
   283  					return perr
   284  				} else {
   285  					return err
   286  				}
   287  			}
   288  		default:
   289  			return err
   290  		}
   291  	}
   292  
   293  	backupLks := libkb.NewLKSecWithClientHalf(clientHalf, ppgen, e.arg.Me.GetUID())
   294  	backupLks.SetServerHalf(libkb.NewLKSecServerHalfZeros())
   295  
   296  	ctext, err := backupLks.EncryptClientHalfRecovery(e.encKey)
   297  	if err != nil {
   298  		return err
   299  	}
   300  
   301  	if err := libkb.PostDeviceLKS(m, backupDev.ID, keybase1.DeviceTypeV2_PAPER, backupLks.GetServerHalf(), backupLks.Generation(), ctext, e.encKey.GetKID()); err != nil {
   302  		return err
   303  	}
   304  
   305  	// push the paper signing key
   306  	sigDel := libkb.Delegator{
   307  		NewKey:       e.sigKey,
   308  		Expire:       libkb.NaclEdDSAExpireIn,
   309  		Me:           e.arg.Me,
   310  		Device:       backupDev,
   311  		Contextified: libkb.NewContextified(e.G()),
   312  	}
   313  	if e.arg.IsEldest {
   314  		sigDel.DelegationType = libkb.DelegationTypeEldest
   315  	} else {
   316  		sigDel.DelegationType = libkb.DelegationTypeSibkey
   317  		sigDel.ExistingKey = e.arg.SigningKey
   318  	}
   319  
   320  	// push the paper encryption key
   321  	sigEnc := libkb.Delegator{
   322  		NewKey:         e.encKey,
   323  		DelegationType: libkb.DelegationTypeSubkey,
   324  		Expire:         libkb.NaclDHExpireIn,
   325  		ExistingKey:    e.sigKey,
   326  		Me:             e.arg.Me,
   327  		Device:         backupDev,
   328  		Contextified:   libkb.NewContextified(e.G()),
   329  	}
   330  
   331  	pukBoxes, err := e.makePerUserKeyBoxes(m)
   332  	if err != nil {
   333  		return err
   334  	}
   335  
   336  	m.Debug("PaperKeyGen#push running delegators")
   337  	return libkb.DelegatorAggregator(m, []libkb.Delegator{sigDel, sigEnc}, nil, pukBoxes, nil, nil)
   338  }
   339  
   340  func (e *PaperKeyGen) makePerUserKeyBoxes(m libkb.MetaContext) ([]keybase1.PerUserKeyBox, error) {
   341  	m.Debug("PaperKeyGen#makePerUserKeyBoxes")
   342  
   343  	var pukBoxes []keybase1.PerUserKeyBox
   344  	pukring, err := e.getPerUserKeyring(m)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	if pukring.HasAnyKeys() {
   349  		if e.arg.EncryptionKey.IsNil() {
   350  			return nil, errors.New("missing encryption key for creating paper key")
   351  		}
   352  		pukBox, err := pukring.PrepareBoxForNewDevice(m,
   353  			e.encKey,            // receiver key: new paper key enc
   354  			e.arg.EncryptionKey) // sender key: this device enc
   355  		if err != nil {
   356  			return nil, err
   357  		}
   358  		pukBoxes = append(pukBoxes, pukBox)
   359  	}
   360  	m.Debug("PaperKeyGen#makePerUserKeyBoxes -> %v", len(pukBoxes))
   361  	return pukBoxes, nil
   362  }
   363  
   364  func (e *PaperKeyGen) getPerUserKeyring(mctx libkb.MetaContext) (ret *libkb.PerUserKeyring, err error) {
   365  	ret = e.arg.PerUserKeyring
   366  	if ret != nil {
   367  		return
   368  	}
   369  	ret, err = e.G().GetPerUserKeyring(mctx.Ctx())
   370  	return
   371  }