Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing EncodedFSKeystore with base32 encoding (#5947) #6955

Merged
merged 3 commits into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ jobs:
name: Cloning
command: |
git clone https://github.com/ipfs/interop.git
git -C interop checkout "fix/disable-repo-interop-test"
git -C interop log -1
- restore_cache:
keys:
Expand Down
86 changes: 53 additions & 33 deletions keystore/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import (
"path/filepath"
"strings"

base32 "encoding/base32"

logging "github.com/ipfs/go-log"
ci "github.com/libp2p/go-libp2p-core/crypto"
)

var log = logging.Logger("keystore")

var codec = base32.StdEncoding.WithPadding(base32.NoPadding)

// Keystore provides a key management interface
type Keystore interface {
// Has returns whether or not a key exist in the Keystore
Expand All @@ -28,30 +32,20 @@ type Keystore interface {
List() ([]string, error)
}

// ErrNoSuchKey is an error message returned when no key of a given name was found.
var ErrNoSuchKey = fmt.Errorf("no key by the given name was found")

// ErrKeyExists is an error message returned when a key already exists
var ErrKeyExists = fmt.Errorf("key by that name already exists, refusing to overwrite")

const keyFilenamePrefix = "key_"

// FSKeystore is a keystore backed by files in a given directory stored on disk.
type FSKeystore struct {
dir string
}

func validateName(name string) error {
if name == "" {
return fmt.Errorf("key names must be at least one character")
}

if strings.Contains(name, "/") {
return fmt.Errorf("key names may not contain slashes")
}

if strings.HasPrefix(name, ".") {
return fmt.Errorf("key names may not begin with a period")
}

return nil
}
hsanjuan marked this conversation as resolved.
Show resolved Hide resolved

// NewFSKeystore returns a new filesystem-backed keystore.
func NewFSKeystore(dir string) (*FSKeystore, error) {
_, err := os.Stat(dir)
if err != nil {
Expand All @@ -68,28 +62,25 @@ func NewFSKeystore(dir string) (*FSKeystore, error) {

// Has returns whether or not a key exist in the Keystore
func (ks *FSKeystore) Has(name string) (bool, error) {
name, err := encode(name)
if err != nil {
return false, err
}

kp := filepath.Join(ks.dir, name)

_, err := os.Stat(kp)
_, err = os.Stat(kp)

if os.IsNotExist(err) {
return false, nil
}

if err != nil {
return false, err
}

if err := validateName(name); err != nil {
return false, err
}

return true, nil
return err == nil, err
}

// Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists
func (ks *FSKeystore) Put(name string, k ci.PrivKey) error {
if err := validateName(name); err != nil {
name, err := encode(name)
if err != nil {
return err
}

Expand Down Expand Up @@ -121,7 +112,8 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error {
// Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey
// otherwise.
func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) {
if err := validateName(name); err != nil {
name, err := encode(name)
if err != nil {
return nil, err
}

Expand All @@ -140,7 +132,8 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) {

// Delete removes a key from the Keystore
func (ks *FSKeystore) Delete(name string) error {
if err := validateName(name); err != nil {
name, err := encode(name)
if err != nil {
return err
}

Expand All @@ -164,13 +157,40 @@ func (ks *FSKeystore) List() ([]string, error) {
list := make([]string, 0, len(dirs))

for _, name := range dirs {
err := validateName(name)
decodedName, err := decode(name)
if err == nil {
list = append(list, name)
list = append(list, decodedName)
} else {
log.Warnf("Ignoring the invalid keyfile: %s", name)
log.Errorf("Ignoring keyfile with invalid encoded filename: %s", name)
}
}

return list, nil
}

func encode(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("key name must be at least one character")
}

encodedName := codec.EncodeToString([]byte(name))
log.Debugf("Encoded key name: %s to: %s", name, encodedName)

return keyFilenamePrefix + strings.ToLower(encodedName), nil
}

func decode(name string) (string, error) {
if !strings.HasPrefix(name, keyFilenamePrefix) {
return "", fmt.Errorf("key's filename has unexpected format")
}

nameWithoutPrefix := strings.ToUpper(name[len(keyFilenamePrefix):])
decodedName, err := codec.DecodeString(nameWithoutPrefix)
if err != nil {
return "", err
}

log.Debugf("Decoded key name: %s to: %s", name, decodedName)

return string(decodedName), nil
}
31 changes: 18 additions & 13 deletions keystore/keystore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,16 @@ func TestKeystoreBasics(t *testing.T) {
t.Fatal(err)
}

if err := ks.Put("..///foo/", k1); err == nil {
t.Fatal("shouldnt be able to put a poorly named key")
if err := ks.Put("..///foo/", k1); err != nil {
t.Fatal(err)
}

if err := ks.Put("", k1); err == nil {
t.Fatal("shouldnt be able to put a key with no name")
}

if err := ks.Put(".foo", k1); err == nil {
t.Fatal("shouldnt be able to put a key with a 'hidden' name")
if err := ks.Put(".foo", k1); err != nil {
t.Fatal(err)
}
}

Expand All @@ -166,12 +166,17 @@ func TestInvalidKeyFiles(t *testing.T) {
t.Fatal(err)
}

err = ioutil.WriteFile(filepath.Join(ks.dir, "valid"), bytes, 0644)
encodedName, err := encode("valid")
if err != nil {
t.Fatal(err)
}

err = ioutil.WriteFile(filepath.Join(ks.dir, ".invalid"), bytes, 0644)
err = ioutil.WriteFile(filepath.Join(ks.dir, encodedName), bytes, 0644)
if err != nil {
t.Fatal(err)
}

err = ioutil.WriteFile(filepath.Join(ks.dir, "z.invalid"), bytes, 0644)
if err != nil {
t.Fatal(err)
}
Expand All @@ -197,10 +202,6 @@ func TestInvalidKeyFiles(t *testing.T) {
if err != nil {
t.Fatal(err)
}

if _, err = ks.Has(".invalid"); err == nil {
t.Fatal("shouldnt be able to put a key with a 'hidden' name")
}
}

func TestNonExistingKey(t *testing.T) {
Expand Down Expand Up @@ -231,12 +232,12 @@ func TestMakeKeystoreNoDir(t *testing.T) {
}

func assertGetKey(ks Keystore, name string, exp ci.PrivKey) error {
out_k, err := ks.Get(name)
outK, err := ks.Get(name)
if err != nil {
return err
}

if !out_k.Equals(exp) {
if !outK.Equals(exp) {
return fmt.Errorf("key we got out didnt match expectation")
}

Expand All @@ -255,7 +256,11 @@ func assertDirContents(dir string, exp []string) error {

var names []string
for _, fi := range finfos {
names = append(names, fi.Name())
decodedName, err := decode(fi.Name())
if err != nil {
return err
}
names = append(names, decodedName)
}

sort.Strings(names)
Expand Down
19 changes: 8 additions & 11 deletions keystore/memkeystore.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package keystore

import ci "github.com/libp2p/go-libp2p-core/crypto"
import (
"errors"

ci "github.com/libp2p/go-libp2p-core/crypto"
)

// MemKeystore is an in memory keystore implementation that is not persisted to
// any backing storage.
type MemKeystore struct {
keys map[string]ci.PrivKey
}

// NewMemKeystore creates a MemKeystore.
func NewMemKeystore() *MemKeystore {
return &MemKeystore{make(map[string]ci.PrivKey)}
}
Expand All @@ -20,8 +25,8 @@ func (mk *MemKeystore) Has(name string) (bool, error) {

// Put store a key in the Keystore
func (mk *MemKeystore) Put(name string, k ci.PrivKey) error {
if err := validateName(name); err != nil {
return err
if name == "" {
return errors.New("key name must be at least one character")
}

_, ok := mk.keys[name]
Expand All @@ -35,10 +40,6 @@ func (mk *MemKeystore) Put(name string, k ci.PrivKey) error {

// Get retrieve a key from the Keystore
func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) {
if err := validateName(name); err != nil {
return nil, err
}

k, ok := mk.keys[name]
if !ok {
return nil, ErrNoSuchKey
Expand All @@ -49,10 +50,6 @@ func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) {

// Delete remove a key from the Keystore
func (mk *MemKeystore) Delete(name string) error {
if err := validateName(name); err != nil {
return err
}

delete(mk.keys, name)
return nil
}
Expand Down
8 changes: 4 additions & 4 deletions keystore/memkeystore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ func TestMemKeyStoreBasics(t *testing.T) {
t.Fatal(err)
}

if err := ks.Put("..///foo/", k1); err == nil {
t.Fatal("shouldnt be able to put a poorly named key")
if err := ks.Put("..///foo/", k1); err != nil {
t.Fatal(err)
}

if err := ks.Put("", k1); err == nil {
t.Fatal("shouldnt be able to put a key with no name")
}

if err := ks.Put(".foo", k1); err == nil {
t.Fatal("shouldnt be able to put a key with a 'hidden' name")
if err := ks.Put(".foo", k1); err != nil {
t.Fatal(err)
}
}
2 changes: 1 addition & 1 deletion repo/fsrepo/fsrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const LockFile = "repo.lock"
var log = logging.Logger("fsrepo")

// version number that we are currently expecting to see
var RepoVersion = 7
var RepoVersion = 9

var migrationInstructions = `See https://github.com/ipfs/fs-repo-migrations/blob/master/run.md
Sorry for the inconvenience. In the future, these will run automatically.`
Expand Down