@cryptotaxi247 / kubo / commits / 059f396ba

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 }