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
6import (
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
20type 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.
36type 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.
48func 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.
56func (e *PaperKeyGen) Name() string {
57	return "PaperKeyGen"
58}
59
60// GetPrereqs returns the engine prereqs.
61func (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.
69func (e *PaperKeyGen) RequiredUIs() []libkb.UIKind {
70	return []libkb.UIKind{}
71}
72
73// SubConsumers returns the other UI consumers for this engine.
74func (e *PaperKeyGen) SubConsumers() []libkb.UIConsumer {
75	return nil
76}
77
78func (e *PaperKeyGen) SigKey() libkb.GenericKey {
79	return e.sigKey
80}
81
82func (e *PaperKeyGen) EncKey() libkb.NaclDHKeyPair {
83	return e.encKey
84}
85
86func (e *PaperKeyGen) DeviceID() keybase1.DeviceID {
87	return e.deviceID
88}
89
90func (e *PaperKeyGen) DeviceWithKeys() *libkb.DeviceWithKeys {
91	return libkb.NewDeviceWithKeysOnly(e.sigKey, e.encKey, libkb.KeychainModeNone)
92}
93
94// Run starts the engine.
95func (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
136func (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
146func (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
164func (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
180func (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
195func (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
232func (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
340func (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
364func (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}
372