1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package 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
11import (
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
20type 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
29type 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
47func 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
70func (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
78func (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
87func (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
102var ErrKeyGenArgNoDefNoCustom = errors.New("invalid args:  NoDefPGPUid set, but no custom PGPUids")
103
104func NewPGPKeyImportEngine(g *libkb.GlobalContext, arg PGPKeyImportEngineArg) *PGPKeyImportEngine {
105	return &PGPKeyImportEngine{arg: arg, Contextified: libkb.NewContextified(g)}
106}
107
108func (e *PGPKeyImportEngine) Name() string {
109	return "PGPKeyImportEngine"
110}
111
112func (e *PGPKeyImportEngine) Prereqs() Prereqs {
113	return Prereqs{}
114}
115
116func (e *PGPKeyImportEngine) RequiredUIs() []libkb.UIKind {
117	return []libkb.UIKind{
118		libkb.LogUIKind,
119		libkb.SecretUIKind,
120	}
121}
122
123func (e *PGPKeyImportEngine) SubConsumers() []libkb.UIConsumer {
124	return nil
125}
126
127func (e *PGPKeyImportEngine) init() (err error) {
128	if e.arg.Gen != nil {
129		err = e.arg.Gen.Init()
130	}
131	return err
132}
133
134func (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.
140func (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
150func (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
166func (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
239func (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.
254func 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
266func (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
313func (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
323func (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
336func (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
351func (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
369func (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
392func (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
417func (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
439func 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
449func (e *PGPKeyImportEngine) GetKID() (kid keybase1.KID) {
450	if e.bundle == nil {
451		return kid
452	}
453	return e.bundle.GetKID()
454}
455