keystore: finish addressing encodedFSKeystore
* Use Go's base32 library * Set repo to version 9 * Resolve linting problems and docs. * Merge EncodedFSKeystore into FSKeystore * Remove name limitations and adjust tests
Hector Sanjuan committed
Mar 5, 2020 at 14:28 UTC
059f396baca17d96b69441d3788e913cf334a36d
5 files changed
+61
-248
keystore/keystore.go
+29
-129
@@ -7,13 +7,16 @@ import (
7
"path/filepath"
8
"strings"
9
10
+ base32 "encoding/base32"
11
+
12
logging "github.com/ipfs/go-log"
13
ci "github.com/libp2p/go-libp2p-core/crypto"
12
- base32 "github.com/whyrusleeping/base32"
14
)
15
16
var log = logging.Logger("keystore")
17
18
+var codec = base32.StdEncoding.WithPadding(base32.NoPadding)
19
+
20
// Keystore provides a key management interface
21
type Keystore interface {
22
// Has returns whether or not a key exist in the Keystore
@@ -29,46 +32,20 @@ type Keystore interface {
32
List() ([]string, error)
33
}
34
35
+// ErrNoSuchKey is an error message returned when no key of a given name was found.
36
var ErrNoSuchKey = fmt.Errorf("no key by the given name was found")
37
+
38
+// ErrKeyExists is an error message returned when a key already exists
39
var ErrKeyExists = fmt.Errorf("key by that name already exists, refusing to overwrite")
40
41
+const keyFilenamePrefix = "key_"
42
+
43
// FSKeystore is a keystore backed by files in a given directory stored on disk.
44
type FSKeystore struct {
45
dir string
46
}
47
40
-func validateName(name string) error {
41
- if name == "" {
42
- return fmt.Errorf("key names must be at least one character")
43
- }
44
-
45
- if strings.Contains(name, "/") {
46
- return fmt.Errorf("key names may not contain slashes")
47
- }
48
-
49
- if strings.HasPrefix(name, ".") {
50
- return fmt.Errorf("key names may not begin with a period")
51
- }
52
-
53
- return nil
54
-}
55
-
56
-// NewKeystore is a factory for getting instance of Keystore interface implementation
57
-func NewKeystore(dir string) (Keystore, error) {
58
- return NewEncodedFSKeystore(dir)
59
-}
60
-
61
-// NewEncodedFSKeystore is a factory for getting instance of EncodedFSKeystore
62
-func NewEncodedFSKeystore(dir string) (*EncodedFSKeystore, error) {
63
- keystore, err := NewFSKeystore(dir)
64
-
65
- if err != nil {
66
- return nil, err
67
- }
68
-
69
- return &EncodedFSKeystore{keystore}, nil
70
-}
71
-
48
+// NewFSKeystore returns a new filesystem-backed keystore.
49
func NewFSKeystore(dir string) (*FSKeystore, error) {
50
_, err := os.Stat(dir)
51
if err != nil {
@@ -85,28 +62,25 @@ func NewFSKeystore(dir string) (*FSKeystore, error) {
62
63
// Has returns whether or not a key exist in the Keystore
64
func (ks *FSKeystore) Has(name string) (bool, error) {
65
+ name, err := encode(name)
66
+ if err != nil {
67
+ return false, err
68
+ }
69
+
70
kp := filepath.Join(ks.dir, name)
71
90
- _, err := os.Stat(kp)
72
+ _, err = os.Stat(kp)
73
74
if os.IsNotExist(err) {
75
return false, nil
76
}
95
-
96
- if err != nil {
97
- return false, err
98
- }
99
-
100
- if err := validateName(name); err != nil {
101
- return false, err
102
- }
103
-
104
- return true, nil
77
+ return err == nil, err
78
}
79
80
// Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists
81
func (ks *FSKeystore) Put(name string, k ci.PrivKey) error {
109
- if err := validateName(name); err != nil {
82
+ name, err := encode(name)
83
+ if err != nil {
84
return err
85
}
86
@@ -138,7 +112,8 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error {
112
// Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey
113
// otherwise.
114
func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) {
141
- if err := validateName(name); err != nil {
115
+ name, err := encode(name)
116
+ if err != nil {
117
return nil, err
118
}
119
@@ -157,7 +132,8 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) {
132
133
// Delete removes a key from the Keystore
134
func (ks *FSKeystore) Delete(name string) error {
160
- if err := validateName(name); err != nil {
135
+ name, err := encode(name)
136
+ if err != nil {
137
return err
138
}
139
@@ -181,25 +157,23 @@ func (ks *FSKeystore) List() ([]string, error) {
157
list := make([]string, 0, len(dirs))
158
159
for _, name := range dirs {
184
- err := validateName(name)
160
+ decodedName, err := decode(name)
161
if err == nil {
186
- list = append(list, name)
162
+ list = append(list, decodedName)
163
} else {
188
- log.Warnf("Ignoring the invalid keyfile: %s", name)
164
+ log.Errorf("Ignoring keyfile with invalid encoded filename: %s", name)
165
}
166
}
167
168
return list, nil
169
}
170
195
-const keyFilenamePrefix = "key_"
196
-
171
func encode(name string) (string, error) {
172
if name == "" {
173
return "", fmt.Errorf("key name must be at least one character")
174
}
175
202
- encodedName := base32.RawStdEncoding.EncodeToString([]byte(name))
176
+ encodedName := codec.EncodeToString([]byte(name))
177
log.Debugf("Encoded key name: %s to: %s", name, encodedName)
178
179
return keyFilenamePrefix + strings.ToLower(encodedName), nil
@@ -211,86 +185,12 @@ func decode(name string) (string, error) {
185
}
186
187
nameWithoutPrefix := strings.ToUpper(name[len(keyFilenamePrefix):])
214
- data, err := base32.RawStdEncoding.DecodeString(nameWithoutPrefix)
215
-
188
+ decodedName, err := codec.DecodeString(nameWithoutPrefix)
189
if err != nil {
190
return "", err
191
}
192
220
- decodedName := string(data[:])
221
-
193
log.Debugf("Decoded key name: %s to: %s", name, decodedName)
194
224
- return decodedName, nil
225
-}
226
-
227
-// EncodedFSKeystore is extension of FSKeystore that encodes the key filenames in base32
228
-type EncodedFSKeystore struct {
229
- *FSKeystore
230
-}
231
-
232
-// Has indicates if key is in keystore
233
-func (ks *EncodedFSKeystore) Has(name string) (bool, error) {
234
- encodedName, err := encode(name)
235
-
236
- if err != nil {
237
- return false, err
238
- }
239
-
240
- return ks.FSKeystore.Has(encodedName)
241
-}
242
-
243
-// Put places key into the keystore
244
-func (ks *EncodedFSKeystore) Put(name string, k ci.PrivKey) error {
245
- encodedName, err := encode(name)
246
-
247
- if err != nil {
248
- return err
249
- }
250
-
251
- return ks.FSKeystore.Put(encodedName, k)
252
-}
253
-
254
-// Get retrieves key by its name from the keystore
255
-func (ks *EncodedFSKeystore) Get(name string) (ci.PrivKey, error) {
256
- encodedName, err := encode(name)
257
-
258
- if err != nil {
259
- return nil, err
260
- }
261
-
262
- return ks.FSKeystore.Get(encodedName)
263
-}
264
-
265
-// Delete removes key from the keystore
266
-func (ks *EncodedFSKeystore) Delete(name string) error {
267
- encodedName, err := encode(name)
268
-
269
- if err != nil {
270
- return err
271
- }
272
-
273
- return ks.FSKeystore.Delete(encodedName)
274
-}
275
-
276
-// List returns list of all keys in keystore
277
-func (ks *EncodedFSKeystore) List() ([]string, error) {
278
- dirs, err := ks.FSKeystore.List()
279
-
280
- if err != nil {
281
- return nil, err
282
- }
283
-
284
- list := make([]string, 0, len(dirs))
285
-
286
- for _, name := range dirs {
287
- decodedName, err := decode(name)
288
- if err == nil {
289
- list = append(list, decodedName)
290
- } else {
291
- log.Warningf("Ignoring keyfile with invalid encoded filename: %s", name)
292
- }
293
- }
294
-
295
- return list, nil
195
+ return string(decodedName), nil
196
}
keystore/keystore_test.go
+18
-102
@@ -132,16 +132,16 @@ func TestKeystoreBasics(t *testing.T) {
132
t.Fatal(err)
133
}
134
135
- if err := ks.Put("..///foo/", k1); err == nil {
136
- t.Fatal("shouldnt be able to put a poorly named key")
135
+ if err := ks.Put("..///foo/", k1); err != nil {
136
+ t.Fatal(err)
137
}
138
139
if err := ks.Put("", k1); err == nil {
140
t.Fatal("shouldnt be able to put a key with no name")
141
}
142
143
- if err := ks.Put(".foo", k1); err == nil {
144
- t.Fatal("shouldnt be able to put a key with a 'hidden' name")
143
+ if err := ks.Put(".foo", k1); err != nil {
144
+ t.Fatal(err)
145
}
146
}
147
@@ -166,12 +166,17 @@ func TestInvalidKeyFiles(t *testing.T) {
166
t.Fatal(err)
167
}
168
169
- err = ioutil.WriteFile(filepath.Join(ks.dir, "valid"), bytes, 0644)
169
+ encodedName, err := encode("valid")
170
+ if err != nil {
171
+ t.Fatal(err)
172
+ }
173
+
174
+ err = ioutil.WriteFile(filepath.Join(ks.dir, encodedName), bytes, 0644)
175
if err != nil {
176
t.Fatal(err)
177
}
178
174
- err = ioutil.WriteFile(filepath.Join(ks.dir, ".invalid"), bytes, 0644)
179
+ err = ioutil.WriteFile(filepath.Join(ks.dir, "z.invalid"), bytes, 0644)
180
if err != nil {
181
t.Fatal(err)
182
}
@@ -197,10 +202,6 @@ func TestInvalidKeyFiles(t *testing.T) {
202
if err != nil {
203
t.Fatal(err)
204
}
200
-
201
- if _, err = ks.Has(".invalid"); err == nil {
202
- t.Fatal("shouldnt be able to put a key with a 'hidden' name")
203
- }
205
}
206
207
func TestNonExistingKey(t *testing.T) {
@@ -231,12 +232,12 @@ func TestMakeKeystoreNoDir(t *testing.T) {
232
}
233
234
func assertGetKey(ks Keystore, name string, exp ci.PrivKey) error {
234
- out_k, err := ks.Get(name)
235
+ outK, err := ks.Get(name)
236
if err != nil {
237
return err
238
}
239
239
- if !out_k.Equals(exp) {
240
+ if !outK.Equals(exp) {
241
return fmt.Errorf("key we got out didnt match expectation")
242
}
243
@@ -255,7 +256,11 @@ func assertDirContents(dir string, exp []string) error {
256
257
var names []string
258
for _, fi := range finfos {
258
- names = append(names, fi.Name())
259
+ decodedName, err := decode(fi.Name())
260
+ if err != nil {
261
+ return err
262
+ }
263
+ names = append(names, decodedName)
264
}
265
266
sort.Strings(names)
@@ -271,92 +276,3 @@ func assertDirContents(dir string, exp []string) error {
276
}
277
return nil
278
}
274
-
275
-func TestEncodedKeystoreBasics(t *testing.T) {
276
- tdir, err := ioutil.TempDir("", "encoded-keystore-test")
277
- if err != nil {
278
- t.Fatal(err)
279
- }
280
-
281
- ks, err := NewEncodedFSKeystore(tdir)
282
- if err != nil {
283
- t.Fatal(err)
284
- }
285
-
286
- l, err := ks.List()
287
- if err != nil {
288
- t.Fatal(err)
289
- }
290
-
291
- if len(l) != 0 {
292
- t.Fatal("expected no keys")
293
- }
294
-
295
- k1 := privKeyOrFatal(t)
296
- k1Name, err := encode("foo")
297
- if err != nil {
298
- t.Fatal(err)
299
- }
300
-
301
- k2 := privKeyOrFatal(t)
302
- k2Name, err := encode("bar")
303
- if err != nil {
304
- t.Fatal(err)
305
- }
306
-
307
- err = ks.Put("foo", k1)
308
- if err != nil {
309
- t.Fatal(err)
310
- }
311
-
312
- err = ks.Put("bar", k2)
313
- if err != nil {
314
- t.Fatal(err)
315
- }
316
-
317
- l, err = ks.List()
318
- if err != nil {
319
- t.Fatal(err)
320
- }
321
-
322
- sort.Strings(l)
323
- if l[0] != "bar" || l[1] != "foo" {
324
- t.Fatal("wrong entries listed")
325
- }
326
-
327
- if err := assertDirContents(tdir, []string{k1Name, k2Name}); err != nil {
328
- t.Fatal(err)
329
- }
330
-
331
- exist, err := ks.Has("foo")
332
- if !exist {
333
- t.Fatal("should know it has a key named foo")
334
- }
335
- if err != nil {
336
- t.Fatal(err)
337
- }
338
-
339
- if err := ks.Delete("bar"); err != nil {
340
- t.Fatal(err)
341
- }
342
-
343
- if err := assertDirContents(tdir, []string{k1Name}); err != nil {
344
- t.Fatal(err)
345
- }
346
-
347
- if err := assertGetKey(ks, "foo", k1); err != nil {
348
- t.Fatal(err)
349
- }
350
-
351
- if err := ks.Put("..///foo/", k1); err != nil {
352
- t.Fatal(err)
353
- }
354
-
355
- if err := ks.Put("", k1); err == nil {
356
- t.Fatal("shouldnt be able to put a key with no name")
357
- }
358
-
359
- if err := ks.Put(".foo", k1); err != nil {
360
- t.Fatal(err)
361
- }
362
-}
keystore/memkeystore.go
+8
-11
@@ -1,6 +1,10 @@
1
package keystore
2
3
-import ci "github.com/libp2p/go-libp2p-core/crypto"
3
+import (
4
+ "errors"
5
+
6
+ ci "github.com/libp2p/go-libp2p-core/crypto"
7
+)
8
9
// MemKeystore is an in memory keystore implementation that is not persisted to
10
// any backing storage.
@@ -8,6 +12,7 @@ type MemKeystore struct {
12
keys map[string]ci.PrivKey
13
}
14
15
+// NewMemKeystore creates a MemKeystore.
16
func NewMemKeystore() *MemKeystore {
17
return &MemKeystore{make(map[string]ci.PrivKey)}
18
}
@@ -20,8 +25,8 @@ func (mk *MemKeystore) Has(name string) (bool, error) {
25
26
// Put store a key in the Keystore
27
func (mk *MemKeystore) Put(name string, k ci.PrivKey) error {
23
- if err := validateName(name); err != nil {
24
- return err
28
+ if name == "" {
29
+ return errors.New("key name must be at least one character")
30
}
31
32
_, ok := mk.keys[name]
@@ -35,10 +40,6 @@ func (mk *MemKeystore) Put(name string, k ci.PrivKey) error {
40
41
// Get retrieve a key from the Keystore
42
func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) {
38
- if err := validateName(name); err != nil {
39
- return nil, err
40
- }
41
-
43
k, ok := mk.keys[name]
44
if !ok {
45
return nil, ErrNoSuchKey
@@ -49,10 +50,6 @@ func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) {
50
51
// Delete remove a key from the Keystore
52
func (mk *MemKeystore) Delete(name string) error {
52
- if err := validateName(name); err != nil {
53
- return err
54
- }
55
-
53
delete(mk.keys, name)
54
return nil
55
}
keystore/memkeystore_test.go
+4
-4
@@ -85,15 +85,15 @@ func TestMemKeyStoreBasics(t *testing.T) {
85
t.Fatal(err)
86
}
87
88
- if err := ks.Put("..///foo/", k1); err == nil {
89
- t.Fatal("shouldnt be able to put a poorly named key")
88
+ if err := ks.Put("..///foo/", k1); err != nil {
89
+ t.Fatal(err)
90
}
91
92
if err := ks.Put("", k1); err == nil {
93
t.Fatal("shouldnt be able to put a key with no name")
94
}
95
96
- if err := ks.Put(".foo", k1); err == nil {
97
- t.Fatal("shouldnt be able to put a key with a 'hidden' name")
96
+ if err := ks.Put(".foo", k1); err != nil {
97
+ t.Fatal(err)
98
}
99
}
repo/fsrepo/fsrepo.go
+2
-2
@@ -36,7 +36,7 @@ const LockFile = "repo.lock"
36
var log = logging.Logger("fsrepo")
37
38
// version number that we are currently expecting to see
39
-var RepoVersion = 8
39
+var RepoVersion = 9
40
41
var migrationInstructions = `See https://github.com/ipfs/fs-repo-migrations/blob/master/run.md
42
Sorry for the inconvenience. In the future, these will run automatically.`
@@ -385,7 +385,7 @@ func (r *FSRepo) openConfig() error {
385
386
func (r *FSRepo) openKeystore() error {
387
ksp := filepath.Join(r.path, "keystore")
388
- ks, err := keystore.NewKeystore(ksp)
388
+ ks, err := keystore.NewFSKeystore(ksp)
389
if err != nil {
390
return err
391
}