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