github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_import_key.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  //
     7  // engine.PGPKeyImportEngine is a class for optionally generating PGP keys,
     8  // and pushing them into the keybase sigchain via the Delegator.
     9  //
    10  
    11  import (
    12  	"bytes"
    13  	"errors"
    14  	"strings"
    15  
    16  	"github.com/keybase/client/go/libkb"
    17  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    18  )
    19  
    20  type PGPKeyImportEngine struct {
    21  	me     *libkb.User
    22  	bundle *libkb.PGPKeyBundle
    23  	arg    PGPKeyImportEngineArg
    24  	epk    string
    25  	del    *libkb.Delegator
    26  	libkb.Contextified
    27  }
    28  
    29  type PGPKeyImportEngineArg struct {
    30  	Gen              *libkb.PGPGenArg
    31  	Pregen           *libkb.PGPKeyBundle
    32  	SigningKey       libkb.GenericKey
    33  	Me               *libkb.User
    34  	Lks              *libkb.LKSec
    35  	NoSave           bool
    36  	PushSecret       bool
    37  	OnlySave         bool
    38  	AllowMulti       bool
    39  	DoExport         bool // export to GPG keychain?
    40  	ExportEncrypted  bool // encrypt secret key before exporting to GPG?
    41  	DoUnlock         bool
    42  	GPGFallback      bool
    43  	PreloadTsec      libkb.Triplesec
    44  	PreloadStreamGen libkb.PassphraseGeneration
    45  }
    46  
    47  func NewPGPKeyImportEngineFromBytes(g *libkb.GlobalContext, key []byte, pushPrivate bool) (eng *PGPKeyImportEngine, err error) {
    48  	var bundle *libkb.PGPKeyBundle
    49  	var w *libkb.Warnings
    50  	if libkb.IsArmored(key) {
    51  		bundle, w, err = libkb.ReadPrivateKeyFromString(string(key))
    52  	} else {
    53  		bundle, w, err = libkb.ReadOneKeyFromBytes(key)
    54  	}
    55  	if err != nil {
    56  		return
    57  	}
    58  	w.Warn(g)
    59  	arg := PGPKeyImportEngineArg{
    60  		Pregen:     bundle,
    61  		PushSecret: pushPrivate,
    62  		AllowMulti: true,
    63  		DoExport:   false,
    64  		DoUnlock:   true,
    65  	}
    66  	eng = NewPGPKeyImportEngine(g, arg)
    67  	return
    68  }
    69  
    70  func (e *PGPKeyImportEngine) loadMe(m libkb.MetaContext) (err error) {
    71  	if e.me = e.arg.Me; e.me != nil {
    72  		return
    73  	}
    74  	e.me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m).WithPublicKeyOptional())
    75  	return err
    76  }
    77  
    78  func (e *PGPKeyImportEngine) generateKey(m libkb.MetaContext) (err error) {
    79  	gen := e.arg.Gen
    80  	if err = gen.CreatePGPIDs(); err != nil {
    81  		return
    82  	}
    83  	e.bundle, err = libkb.GeneratePGPKeyBundle(m.G(), *gen, m.UIs().LogUI)
    84  	return
    85  }
    86  
    87  func (e *PGPKeyImportEngine) saveLKS(m libkb.MetaContext) (err error) {
    88  
    89  	defer m.Trace("PGPKeyImportEngine::saveLKS", &err)()
    90  
    91  	lks := e.arg.Lks
    92  	if lks == nil {
    93  		lks, err = libkb.NewLKSecForEncrypt(m, m.UIs().SecretUI, e.me.GetUID())
    94  		if err != nil {
    95  			return err
    96  		}
    97  	}
    98  	_, err = libkb.WriteLksSKBToKeyring(m, e.bundle, lks)
    99  	return
   100  }
   101  
   102  var ErrKeyGenArgNoDefNoCustom = errors.New("invalid args:  NoDefPGPUid set, but no custom PGPUids")
   103  
   104  func NewPGPKeyImportEngine(g *libkb.GlobalContext, arg PGPKeyImportEngineArg) *PGPKeyImportEngine {
   105  	return &PGPKeyImportEngine{arg: arg, Contextified: libkb.NewContextified(g)}
   106  }
   107  
   108  func (e *PGPKeyImportEngine) Name() string {
   109  	return "PGPKeyImportEngine"
   110  }
   111  
   112  func (e *PGPKeyImportEngine) Prereqs() Prereqs {
   113  	return Prereqs{}
   114  }
   115  
   116  func (e *PGPKeyImportEngine) RequiredUIs() []libkb.UIKind {
   117  	return []libkb.UIKind{
   118  		libkb.LogUIKind,
   119  		libkb.SecretUIKind,
   120  	}
   121  }
   122  
   123  func (e *PGPKeyImportEngine) SubConsumers() []libkb.UIConsumer {
   124  	return nil
   125  }
   126  
   127  func (e *PGPKeyImportEngine) init() (err error) {
   128  	if e.arg.Gen != nil {
   129  		err = e.arg.Gen.Init()
   130  	}
   131  	return err
   132  }
   133  
   134  func (e *PGPKeyImportEngine) testExisting() (err error) {
   135  	return PGPCheckMulti(e.me, e.arg.AllowMulti)
   136  }
   137  
   138  // checkPregenPrivate makes sure that the pregenerated key is a
   139  // private key.
   140  func (e *PGPKeyImportEngine) checkPregenPrivate() error {
   141  	if e.arg.Pregen == nil {
   142  		return nil
   143  	}
   144  	if e.arg.Pregen.HasSecretKey() || e.arg.GPGFallback {
   145  		return nil
   146  	}
   147  	return libkb.NoSecretKeyError{}
   148  }
   149  
   150  func (e *PGPKeyImportEngine) checkExistingKey(m libkb.MetaContext) error {
   151  	// Check if we have a public key that matches
   152  	pgps := e.me.GetActivePGPKeys(false)
   153  	for _, key := range pgps {
   154  		if e.GetKID() != key.GetKID() {
   155  			continue
   156  		}
   157  
   158  		e.G().Log.Info("Key %s already exists. Only importing the private key.", e.GetKID())
   159  		e.arg.OnlySave = true
   160  		break
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func (e *PGPKeyImportEngine) Run(m libkb.MetaContext) (err error) {
   167  	defer m.Trace("PGPKeyImportEngine::Run", &err)()
   168  
   169  	if err = e.init(); err != nil {
   170  		return err
   171  	}
   172  
   173  	if err = e.loadMe(m); err != nil {
   174  		switch err.(type) {
   175  		case libkb.SelfNotFoundError:
   176  			return libkb.LoginRequiredError{}
   177  		default:
   178  			return err
   179  		}
   180  	}
   181  
   182  	if e.arg.PushSecret {
   183  		if err = e.checkRandomPassword(m); err != nil {
   184  			return err
   185  		}
   186  	}
   187  
   188  	if err = e.checkPregenPrivate(); err != nil {
   189  		return err
   190  	}
   191  
   192  	if !e.arg.OnlySave {
   193  		if err = e.testExisting(); err != nil {
   194  			return err
   195  		}
   196  
   197  		if err = e.loadDelegator(m); err != nil {
   198  			switch err.(type) {
   199  			case libkb.NoUsernameError:
   200  				return libkb.LoginRequiredError{}
   201  			default:
   202  				return err
   203  			}
   204  		}
   205  	}
   206  
   207  	if err = e.generate(m); err != nil {
   208  		return err
   209  	}
   210  
   211  	if err = e.unlock(m); err != nil {
   212  		return err
   213  	}
   214  
   215  	if err := e.checkExistingKey(m); err != nil {
   216  		return err
   217  	}
   218  
   219  	if err = e.saveKey(m); err != nil {
   220  		return err
   221  	}
   222  
   223  	if !e.arg.OnlySave {
   224  		if err = e.push(m); err != nil {
   225  			return err
   226  		}
   227  		if err = e.exportToGPG(m); err != nil {
   228  			return GPGExportingError{err, true /* inPGPGen */}
   229  		}
   230  	} else if e.arg.PushSecret {
   231  		if err = e.pushSecretOnly(m); err != nil {
   232  			return err
   233  		}
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  func (e *PGPKeyImportEngine) checkRandomPassword(mctx libkb.MetaContext) error {
   240  	passphraseState, err := libkb.LoadPassphraseState(mctx)
   241  	if err != nil {
   242  		return err
   243  	}
   244  	if passphraseState == keybase1.PassphraseState_RANDOM {
   245  		return libkb.NewPushSecretWithoutPasswordError("You need to set your password first before uploading secret keys")
   246  	}
   247  	return nil
   248  }
   249  
   250  // clonePGPKeyBundle returns an approximate deep copy of PGPKeyBundle
   251  // by exporting and re-importing PGPKeyBundle. If PGP key contains
   252  // something that is not supported by either go-crypto exporter or
   253  // importer, that information will be lost.
   254  func clonePGPKeyBundle(bundle *libkb.PGPKeyBundle) (*libkb.PGPKeyBundle, error) {
   255  	var buf bytes.Buffer
   256  	if err := bundle.SerializePrivate(&buf); err != nil {
   257  		return nil, err
   258  	}
   259  	res, _, err := libkb.ReadOneKeyFromBytes(buf.Bytes())
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	return res, nil
   264  }
   265  
   266  func (e *PGPKeyImportEngine) exportToGPG(m libkb.MetaContext) (err error) {
   267  	if !e.arg.DoExport || e.arg.Pregen != nil {
   268  		m.Debug("| Skipping export to GPG")
   269  		return nil
   270  	}
   271  	gpg := e.G().GetGpgClient()
   272  
   273  	ok, err := gpg.CanExec(m)
   274  	if err != nil {
   275  		m.Debug("Not saving new key to GPG. Error in gpg.CanExec(): %s", err)
   276  		// libkb/util_*.go:canExec() can return generic errors, just ignore them
   277  		// in this situation since export to gpg is on by default in the client
   278  		// pgp gen command.
   279  		return nil
   280  	}
   281  	if !ok {
   282  		m.Debug("Not saving new key to GPG since no gpg install was found")
   283  		return nil
   284  	}
   285  
   286  	exportedBundle := e.bundle
   287  
   288  	if e.arg.ExportEncrypted {
   289  		m.Debug("Encrypting key with passphrase before exporting")
   290  		desc := "Exporting key to GPG keychain. Enter passphrase to protect the key. Secure passphrases have at least 8 characters."
   291  		pRes, err := GetPGPExportPassphrase(m, m.UIs().SecretUI, desc)
   292  		if err != nil {
   293  			return err
   294  		}
   295  		// Avoid mutating e.bundle.
   296  		if exportedBundle, err = clonePGPKeyBundle(e.bundle); err != nil {
   297  			return err
   298  		}
   299  		if err = libkb.EncryptPGPKey(exportedBundle.Entity, pRes.Passphrase); err != nil {
   300  			return err
   301  		}
   302  	}
   303  
   304  	// If key is encrypted, use batch mode in gpg so it does not ask
   305  	// for passphrase to re-encrypt to its internal representation.
   306  	err = gpg.ExportKey(m, *exportedBundle, true /* private */, e.arg.ExportEncrypted /* batch */)
   307  	if err == nil {
   308  		m.UIs().LogUI.Info("Exported new key to the local GPG keychain")
   309  	}
   310  	return err
   311  }
   312  
   313  func (e *PGPKeyImportEngine) unlock(m libkb.MetaContext) (err error) {
   314  	defer m.Trace("PGPKeyImportEngine::unlock", &err)()
   315  	if e.arg.Pregen == nil || !e.arg.DoUnlock || !e.arg.Pregen.HasSecretKey() {
   316  		m.Debug("| short circuit unlock function")
   317  	} else {
   318  		err = e.arg.Pregen.Unlock(m, "import into private keychain", m.UIs().SecretUI)
   319  	}
   320  	return err
   321  }
   322  
   323  func (e *PGPKeyImportEngine) loadDelegator(m libkb.MetaContext) (err error) {
   324  
   325  	e.del = &libkb.Delegator{
   326  		ExistingKey:    e.arg.SigningKey,
   327  		Me:             e.me,
   328  		Expire:         libkb.KeyExpireIn,
   329  		DelegationType: libkb.DelegationTypeSibkey,
   330  		Contextified:   libkb.NewContextified(e.G()),
   331  	}
   332  
   333  	return e.del.LoadSigningKey(m, m.UIs().SecretUI)
   334  }
   335  
   336  func (e *PGPKeyImportEngine) generate(m libkb.MetaContext) (err error) {
   337  	defer m.Trace("PGP::Generate", &err)()
   338  
   339  	m.Debug("| GenerateKey")
   340  	if e.arg.Pregen != nil {
   341  		e.bundle = e.arg.Pregen
   342  	} else if e.arg.Gen == nil {
   343  		err = libkb.InternalError{Msg: "PGPKeyImportEngine: need either Gen or Pregen"}
   344  		return
   345  	} else if err = e.generateKey(m); err != nil {
   346  		return
   347  	}
   348  	return
   349  }
   350  
   351  func (e *PGPKeyImportEngine) saveKey(m libkb.MetaContext) (err error) {
   352  	defer m.Trace("PGP::saveKey", &err)()
   353  
   354  	m.Debug("| WriteKey (hasSecret = %v)", e.bundle.HasSecretKey())
   355  	if !e.arg.NoSave && e.bundle.HasSecretKey() {
   356  		if err = e.saveLKS(m); err != nil {
   357  			return
   358  		}
   359  	}
   360  
   361  	if e.arg.PushSecret {
   362  		if err = e.prepareSecretPush(m); err != nil {
   363  			return
   364  		}
   365  	}
   366  	return
   367  }
   368  
   369  func (e *PGPKeyImportEngine) prepareSecretPush(m libkb.MetaContext) error {
   370  	var tsec libkb.Triplesec
   371  	var gen libkb.PassphraseGeneration
   372  	if e.arg.PreloadTsec != nil && e.arg.PreloadStreamGen > 0 {
   373  		tsec = e.arg.PreloadTsec
   374  		gen = e.arg.PreloadStreamGen
   375  	} else {
   376  		var err error
   377  		tsec, gen, err = libkb.GetTriplesecMaybePrompt(m)
   378  		if err != nil {
   379  			return err
   380  		}
   381  	}
   382  
   383  	skb, err := e.bundle.ToServerSKB(m.G(), tsec, gen)
   384  	if err != nil {
   385  		return err
   386  	}
   387  	e.epk, err = skb.ArmoredEncode()
   388  
   389  	return err
   390  }
   391  
   392  func (e *PGPKeyImportEngine) push(m libkb.MetaContext) (err error) {
   393  	defer m.Trace("PGP#Push", &err)()
   394  	if e.arg.GPGFallback {
   395  		e.bundle.GPGFallbackKey = libkb.NewGPGKey(
   396  			m.G(),
   397  			e.bundle.GetFingerprintP(),
   398  			e.bundle.GetKID(),
   399  			m.UIs().GPGUI,
   400  			m.UIs().ClientType)
   401  	}
   402  	e.del.NewKey = e.bundle
   403  	e.del.EncodedPrivateKey = e.epk
   404  	if err = e.del.Run(m); err != nil {
   405  		return err
   406  	}
   407  
   408  	m.UIs().LogUI.Info("Generated new PGP key:")
   409  	d := e.bundle.VerboseDescription()
   410  	for _, line := range strings.Split(d, "\n") {
   411  		m.UIs().LogUI.Info("  %s", line)
   412  	}
   413  
   414  	return nil
   415  }
   416  
   417  func (e *PGPKeyImportEngine) pushSecretOnly(m libkb.MetaContext) (err error) {
   418  	defer m.Trace("PGP#PushSecretOnly", &err)()
   419  
   420  	m.UIs().LogUI.Info("Only pushing encrypted private key to Keybase server")
   421  
   422  	hargs := libkb.HTTPArgs{
   423  		"private_key": libkb.S{Val: e.epk},
   424  	}
   425  	arg := libkb.APIArg{
   426  		Endpoint:    "key/add",
   427  		SessionType: libkb.APISessionTypeREQUIRED,
   428  		Args:        hargs,
   429  	}
   430  	_, err = m.G().API.Post(m, arg)
   431  	if err != nil {
   432  		return err
   433  	}
   434  
   435  	m.UIs().LogUI.Info("Success! Pushed encrypted private key")
   436  	return nil
   437  }
   438  
   439  func PGPCheckMulti(me *libkb.User, allowMulti bool) (err error) {
   440  	if allowMulti {
   441  		return
   442  	}
   443  	if pgps := me.GetActivePGPKeys(false); len(pgps) > 0 {
   444  		err = libkb.KeyExistsError{Key: pgps[0].GetFingerprintP()}
   445  	}
   446  	return
   447  }
   448  
   449  func (e *PGPKeyImportEngine) GetKID() (kid keybase1.KID) {
   450  	if e.bundle == nil {
   451  		return kid
   452  	}
   453  	return e.bundle.GetKID()
   454  }