github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_export_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  	"fmt"
    15  
    16  	"github.com/keybase/client/go/kbcrypto"
    17  	"github.com/keybase/client/go/libkb"
    18  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    19  )
    20  
    21  type queryType int
    22  
    23  const (
    24  	unset queryType = iota
    25  	fingerprint
    26  	kid
    27  	either
    28  )
    29  
    30  type PGPKeyExportEngine struct {
    31  	libkb.Contextified
    32  	arg       keybase1.PGPQuery
    33  	encrypted bool
    34  	qtype     queryType
    35  	res       []keybase1.KeyInfo
    36  	me        *libkb.User
    37  }
    38  
    39  func (e *PGPKeyExportEngine) Prereqs() Prereqs {
    40  	return Prereqs{
    41  		Device: true,
    42  	}
    43  }
    44  
    45  func (e *PGPKeyExportEngine) Name() string {
    46  	return "PGPKeyExportEngine"
    47  }
    48  
    49  func (e *PGPKeyExportEngine) RequiredUIs() []libkb.UIKind {
    50  	return []libkb.UIKind{
    51  		libkb.SecretUIKind,
    52  	}
    53  }
    54  
    55  func (e *PGPKeyExportEngine) SubConsumers() []libkb.UIConsumer {
    56  	return nil
    57  }
    58  
    59  func (e *PGPKeyExportEngine) Results() []keybase1.KeyInfo {
    60  	return e.res
    61  }
    62  
    63  func NewPGPKeyExportEngine(g *libkb.GlobalContext, arg keybase1.PGPExportArg) *PGPKeyExportEngine {
    64  	return &PGPKeyExportEngine{
    65  		arg:          arg.Options,
    66  		qtype:        either,
    67  		encrypted:    arg.Encrypted,
    68  		Contextified: libkb.NewContextified(g),
    69  	}
    70  }
    71  
    72  func NewPGPKeyExportByKIDEngine(g *libkb.GlobalContext, arg keybase1.PGPExportByKIDArg) *PGPKeyExportEngine {
    73  	return &PGPKeyExportEngine{
    74  		arg:          arg.Options,
    75  		qtype:        kid,
    76  		encrypted:    arg.Encrypted,
    77  		Contextified: libkb.NewContextified(g),
    78  	}
    79  }
    80  
    81  func NewPGPKeyExportByFingerprintEngine(g *libkb.GlobalContext, arg keybase1.PGPExportByFingerprintArg) *PGPKeyExportEngine {
    82  	return &PGPKeyExportEngine{
    83  		arg:          arg.Options,
    84  		qtype:        fingerprint,
    85  		encrypted:    arg.Encrypted,
    86  		Contextified: libkb.NewContextified(g),
    87  	}
    88  }
    89  
    90  func (e *PGPKeyExportEngine) pushRes(fp libkb.PGPFingerprint, key string, desc string) {
    91  	e.res = append(e.res, keybase1.KeyInfo{
    92  		Fingerprint: fp.String(),
    93  		Key:         key,
    94  		Desc:        desc,
    95  	})
    96  }
    97  
    98  func (e *PGPKeyExportEngine) queryMatch(k libkb.GenericKey) bool {
    99  	if len(e.arg.Query) == 0 {
   100  		return true
   101  	}
   102  	var match bool
   103  	switch e.qtype {
   104  	case either:
   105  		match = libkb.KeyMatchesQuery(k, e.arg.Query, e.arg.ExactMatch)
   106  	case fingerprint:
   107  		if fp := libkb.GetPGPFingerprintFromGenericKey(k); fp != nil {
   108  			match = fp.Match(e.arg.Query, e.arg.ExactMatch)
   109  		}
   110  	case kid:
   111  		match = k.GetKID().Match(e.arg.Query, e.arg.ExactMatch)
   112  	}
   113  	return match
   114  }
   115  
   116  func (e *PGPKeyExportEngine) exportPublic() (err error) {
   117  	keys := e.me.GetActivePGPKeys(false)
   118  	for _, k := range keys {
   119  		fp := k.GetFingerprintP()
   120  		s, err := k.Encode()
   121  		if fp == nil || err != nil {
   122  			continue
   123  		}
   124  		if !e.queryMatch(k) {
   125  			continue
   126  		}
   127  		e.pushRes(*fp, s, k.VerboseDescription())
   128  	}
   129  	return
   130  }
   131  
   132  func (e *PGPKeyExportEngine) exportSecret(m libkb.MetaContext) error {
   133  	ska := libkb.SecretKeyArg{
   134  		Me:         e.me,
   135  		KeyType:    libkb.PGPKeyType,
   136  		KeyQuery:   e.arg.Query,
   137  		ExactMatch: e.arg.ExactMatch,
   138  	}
   139  	key, skb, err := m.G().Keyrings.GetSecretKeyAndSKBWithPrompt(m, m.SecretKeyPromptArg(ska, "key export"))
   140  	if err != nil {
   141  		if _, ok := err.(libkb.NoSecretKeyError); ok {
   142  			// if no secret key found, don't return an error, just let
   143  			// the result be empty
   144  			return nil
   145  		}
   146  		return err
   147  	}
   148  	fp := libkb.GetPGPFingerprintFromGenericKey(key)
   149  	if fp == nil {
   150  		return kbcrypto.BadKeyError{Msg: "no fingerprint found"}
   151  	}
   152  
   153  	if !e.queryMatch(key) {
   154  		return nil
   155  	}
   156  
   157  	if _, ok := key.(*libkb.PGPKeyBundle); !ok {
   158  		return kbcrypto.BadKeyError{Msg: "Expected a PGP key"}
   159  	}
   160  
   161  	raw := skb.RawUnlockedKey()
   162  	if raw == nil {
   163  		return kbcrypto.BadKeyError{Msg: "can't get raw representation of key"}
   164  	}
   165  
   166  	if e.encrypted {
   167  		// Make encrypted PGP key bundle using provided passphrase.
   168  		// Key will be reimported from bytes so we don't mutate SKB.
   169  		raw, err = e.encryptKey(m, raw)
   170  		if err != nil {
   171  			return err
   172  		}
   173  	}
   174  
   175  	ret, err := libkb.PGPKeyRawToArmored(raw, true)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	e.pushRes(*fp, ret, "")
   181  
   182  	return nil
   183  }
   184  
   185  func GetPGPExportPassphrase(m libkb.MetaContext, ui libkb.SecretUI, desc string) (keybase1.GetPassphraseRes, error) {
   186  	pRes, err := libkb.GetSecret(m, ui, "PGP key passphrase", desc, "", false)
   187  	if err != nil {
   188  		return keybase1.GetPassphraseRes{}, err
   189  	}
   190  
   191  	desc = "Please reenter your passphrase for confirmation"
   192  	pRes2, err := libkb.GetSecret(m, ui, "PGP key passphrase", desc, "", false)
   193  	if err != nil {
   194  		return keybase1.GetPassphraseRes{}, err
   195  	}
   196  	if pRes.Passphrase != pRes2.Passphrase {
   197  		return keybase1.GetPassphraseRes{}, errors.New("Passphrase mismatch")
   198  	}
   199  
   200  	return pRes, nil
   201  }
   202  
   203  func (e *PGPKeyExportEngine) encryptKey(m libkb.MetaContext, raw []byte) ([]byte, error) {
   204  	entity, _, err := libkb.ReadOneKeyFromBytes(raw)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	if entity.PrivateKey == nil {
   210  		return nil, kbcrypto.BadKeyError{Msg: "No secret part in PGP key."}
   211  	}
   212  
   213  	desc := "Enter passphrase to protect your PGP key. Secure passphrases have at least 8 characters."
   214  	pRes, err := GetPGPExportPassphrase(m, m.UIs().SecretUI, desc)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	if err = libkb.EncryptPGPKey(entity.Entity, pRes.Passphrase); err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	var buf bytes.Buffer
   224  	if err = entity.SerializePrivate(&buf); err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	return buf.Bytes(), nil
   229  }
   230  
   231  func (e *PGPKeyExportEngine) loadMe(m libkb.MetaContext) (err error) {
   232  	e.me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m).WithPublicKeyOptional())
   233  	return
   234  }
   235  
   236  func (e *PGPKeyExportEngine) Run(m libkb.MetaContext) (err error) {
   237  	defer m.Trace("PGPKeyExportEngine::Run", &err)()
   238  
   239  	if e.qtype == unset {
   240  		return fmt.Errorf("PGPKeyExportEngine: query type not set")
   241  	}
   242  
   243  	if err = e.loadMe(m); err != nil {
   244  		return
   245  	}
   246  
   247  	if e.arg.Secret {
   248  		err = e.exportSecret(m)
   249  	} else {
   250  		err = e.exportPublic()
   251  	}
   252  
   253  	return
   254  }