1package engine
2
3import (
4	"fmt"
5	"os"
6
7	"github.com/keybase/client/go/libkb"
8	keybase1 "github.com/keybase/client/go/protocol/keybase1"
9)
10
11func retryOnEphemeralRace(mctx libkb.MetaContext, fn func(mctx libkb.MetaContext) error) (err error) {
12	for attempt := 0; attempt < 5; attempt++ {
13		if err = fn(mctx); err == nil {
14			return nil
15		}
16		if !libkb.IsEphemeralRetryableError(err) {
17			return err
18		}
19		mctx.Debug("retryOnEphemeralRace found a retryable error on try %d: %v",
20			attempt, err)
21	}
22	return err
23}
24
25// ephemeralKeyReboxer will rebox the lastest userEK while provisioning
26// devices.  The provisionee generates a deviceEK seed so that the provisioner
27// can rebox the latest userEK for the new deviceKID.  The provisionee posts
28// the userEKBox and deviceEKStatement when posting the new device keys.  Once
29// fully provisioned the provisionee saves the new deviceEK to storage,
30// encrypted by the newly created device.
31type ephemeralKeyReboxer struct {
32	deviceEKSeed      keybase1.Bytes32
33	seedGenerated     bool
34	deviceEKStatement keybase1.DeviceEkStatement
35	userEKBox         *keybase1.UserEkBoxed
36}
37
38func newEphemeralKeyReboxer() *ephemeralKeyReboxer {
39	return &ephemeralKeyReboxer{}
40}
41
42func (e *ephemeralKeyReboxer) getDeviceEKSeed(mctx libkb.MetaContext) (err error) {
43	if ekLib := mctx.G().GetEKLib(); ekLib != nil && !e.seedGenerated {
44		e.deviceEKSeed, err = ekLib.NewEphemeralSeed()
45		if err != nil {
46			return err
47		}
48		e.seedGenerated = true
49	}
50	return nil
51}
52
53func (e *ephemeralKeyReboxer) getDeviceEKKID(mctx libkb.MetaContext) (kid keybase1.KID, err error) {
54	if !e.seedGenerated {
55		if err := e.getDeviceEKSeed(mctx); err != nil {
56			return "", err
57		}
58	}
59	if ekLib := mctx.G().GetEKLib(); ekLib != nil {
60		ekPair := ekLib.DeriveDeviceDHKey(e.deviceEKSeed)
61		return ekPair.GetKID(), nil
62	}
63	return "", nil
64}
65
66func (e *ephemeralKeyReboxer) getReboxArg(mctx libkb.MetaContext, userEKBox *keybase1.UserEkBoxed,
67	deviceID keybase1.DeviceID, signingKey libkb.GenericKey) (userEKReboxArg *keybase1.UserEkReboxArg, err error) {
68	defer mctx.Trace("ephemeralKeyReboxer#getReboxArg", &err)()
69
70	ekLib := mctx.G().GetEKLib()
71	if ekLib == nil {
72		return nil, nil
73	}
74
75	if userEKBox == nil { // We will create EKs after provisioning in the normal way
76		mctx.Debug("userEKBox nil, no ephemeral keys created during provisioning")
77		return nil, nil
78	}
79
80	deviceEKStatement, deviceEKStatementSig, err := ekLib.SignedDeviceEKStatementFromSeed(
81		mctx, userEKBox.DeviceEkGeneration, e.deviceEKSeed, signingKey)
82	if err != nil {
83		return nil, err
84	}
85
86	userEKReboxArg = &keybase1.UserEkReboxArg{
87		UserEkBoxMetadata: keybase1.UserEkBoxMetadata{
88			Box:                 userEKBox.Box,
89			RecipientDeviceID:   deviceID,
90			RecipientGeneration: userEKBox.DeviceEkGeneration,
91		},
92		DeviceID:             deviceID,
93		DeviceEkStatementSig: deviceEKStatementSig,
94	}
95
96	e.deviceEKStatement = deviceEKStatement
97	e.userEKBox = userEKBox
98
99	return userEKReboxArg, nil
100}
101
102func (e *ephemeralKeyReboxer) storeEKs(mctx libkb.MetaContext) (err error) {
103	defer mctx.Trace("ephemeralKeyReboxer#storeEKs", &err)()
104	if ekLib := mctx.G().GetEKLib(); ekLib == nil {
105		return nil
106	}
107	if e.userEKBox == nil {
108		mctx.Debug("userEKBox nil, no ephemeral keys to store")
109		return nil
110	}
111
112	if !e.seedGenerated {
113		return fmt.Errorf("Unable to store EKs with out generating a seed first")
114	}
115
116	deviceEKStorage := mctx.G().GetDeviceEKStorage()
117	metadata := e.deviceEKStatement.CurrentDeviceEkMetadata
118	if err = deviceEKStorage.Put(mctx, metadata.Generation, keybase1.DeviceEk{
119		Seed:     e.deviceEKSeed,
120		Metadata: metadata,
121	}); err != nil {
122		return err
123	}
124
125	userEKBoxStorage := mctx.G().GetUserEKBoxStorage()
126	return userEKBoxStorage.Put(mctx, e.userEKBox.Metadata.Generation, *e.userEKBox)
127}
128
129func makeUserEKBoxForProvisionee(mctx libkb.MetaContext, kid keybase1.KID) (*keybase1.UserEkBoxed, error) {
130	ekLib := mctx.G().GetEKLib()
131	if ekLib == nil {
132		return nil, nil
133	}
134	ekPair, err := libkb.ImportKeypairFromKID(kid)
135	if err != nil {
136		return nil, err
137	}
138	receiverKey, ok := ekPair.(libkb.NaclDHKeyPair)
139	if !ok {
140		return nil, fmt.Errorf("Unexpected receiver key type")
141	}
142	// This is hardcoded to 1 since we're provisioning a new device.
143	deviceEKGeneration := keybase1.EkGeneration(1)
144	return ekLib.BoxLatestUserEK(mctx, receiverKey, deviceEKGeneration)
145}
146
147func verifyLocalStorage(m libkb.MetaContext, username string, uid keybase1.UID) {
148	m.Debug("verifying local storage")
149	defer m.Debug("done verifying local storage")
150	normUsername := libkb.NewNormalizedUsername(username)
151
152	// check config.json looks ok
153	verifyRegularFile(m, "config", m.G().Env.GetConfigFilename())
154	cr := m.G().Env.GetConfig()
155	if cr.GetUsername() != normUsername {
156		m.Debug("config username %q doesn't match engine username %q", cr.GetUsername(), normUsername)
157	}
158	if cr.GetUID().NotEqual(uid) {
159		m.Debug("config uid %q doesn't match engine uid %q", cr.GetUID(), uid)
160	}
161
162	// check keys in secretkeys.mpack
163	verifyRegularFile(m, "secretkeys", m.G().SKBFilenameForUser(normUsername))
164
165	// check secret stored
166	secret, err := m.G().SecretStore().RetrieveSecret(m, normUsername)
167	if err != nil {
168		m.Debug("failed to retrieve secret for %s: %s", username, err)
169	}
170	if secret.IsNil() || len(secret.Bytes()) == 0 {
171		m.Debug("retrieved nil/empty secret for %s", username)
172	}
173}
174
175func verifyRegularFile(m libkb.MetaContext, name, filename string) {
176	info, err := os.Stat(filename)
177	if err != nil {
178		m.Debug("stat %s file %q error: %s", name, filename, err)
179		return
180	}
181
182	m.Debug("%s file %q size: %d", name, filename, info.Size())
183	if !info.Mode().IsRegular() {
184		m.Debug("%s file %q not regular: %s", name, filename, info.Mode())
185	}
186}
187