diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go index 0086bd0d6296..dc836566991e 100644 --- a/core/coreapi/interface/interface.go +++ b/core/coreapi/interface/interface.go @@ -148,13 +148,13 @@ type KeyAPI interface { // name and returns a base58 encoded multihash of it's public key Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) - // WithAlgorithm is an option for Generate which specifies which algorithm + // WithType is an option for Generate which specifies which algorithm // should be used for the key. Default is options.RSAKey // - // Supported algorithms: + // Supported key types: // * options.RSAKey // * options.Ed25519Key - WithAlgorithm(algorithm string) options.KeyGenerateOption + WithType(algorithm string) options.KeyGenerateOption // WithSize is an option for Generate which specifies the size of the key to // generated. Default is 0 diff --git a/core/coreapi/interface/options/key.go b/core/coreapi/interface/options/key.go index c84f0f8f8b8f..114361875a5f 100644 --- a/core/coreapi/interface/options/key.go +++ b/core/coreapi/interface/options/key.go @@ -3,6 +3,8 @@ package options const ( RSAKey = "rsa" Ed25519Key = "ed25519" + + DefaultRSALen = 2048 ) type KeyGenerateSettings struct { @@ -20,7 +22,7 @@ type KeyRenameOption func(*KeyRenameSettings) error func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) { options := &KeyGenerateSettings{ Algorithm: RSAKey, - Size: 0, + Size: -1, } for _, opt := range opts { @@ -48,7 +50,7 @@ func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) { type KeyOptions struct{} -func (api *KeyOptions) WithAlgorithm(algorithm string) KeyGenerateOption { +func (api *KeyOptions) WithType(algorithm string) KeyGenerateOption { return func(settings *KeyGenerateSettings) error { settings.Algorithm = algorithm return nil diff --git a/core/coreapi/key.go b/core/coreapi/key.go index 99ae490f54e4..b5a0e3308baa 100644 --- a/core/coreapi/key.go +++ b/core/coreapi/key.go @@ -29,7 +29,7 @@ func (k *key) Name() string { } func (k *key) Path() coreiface.Path { - return &path{path: ipfspath.FromString(ipfspath.Join([]string{"/ipns/", k.peerId}))} + return &path{path: ipfspath.FromString(ipfspath.Join([]string{"/ipns", k.peerId}))} } func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) { @@ -38,13 +38,22 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key return nil, err } + if name == "self" { + return nil, fmt.Errorf("cannot overwrite key with name 'self'") + } + + _, err = api.node.Repo.Keystore().Get(name) + if err == nil { + return nil, fmt.Errorf("key with name '%s' already exists", name) + } + var sk crypto.PrivKey var pk crypto.PubKey switch options.Algorithm { case "rsa": - if options.Size == 0 { - return nil, fmt.Errorf("please specify a key size with WithSize option") + if options.Size == -1 { + options.Size = caopts.DefaultRSALen } priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.RSA, options.Size, rand.Reader) @@ -76,7 +85,7 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key return nil, err } - return &key{name, pid.String()}, nil + return &key{name, pid.Pretty()}, nil } func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) { diff --git a/core/coreapi/key_test.go b/core/coreapi/key_test.go new file mode 100644 index 000000000000..45f365da9f71 --- /dev/null +++ b/core/coreapi/key_test.go @@ -0,0 +1,416 @@ +package coreapi_test + +import ( + "context" + "strings" + "testing" + + opts "github.com/ipfs/go-ipfs/core/coreapi/interface/options" +) + +func TestListSelf(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + return + } + + keys, err := api.Key().List(ctx) + if err != nil { + t.Fatalf("failed to list keys: %s", err) + return + } + + if len(keys) != 1 { + t.Fatalf("there should be 1 key (self), got %d", len(keys)) + return + } + + if keys[0].Name() != "self" { + t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name()) + } + + if keys[0].Path().String() != "/ipns/Qmfoo" { + t.Errorf("expected the key to have path '/ipns/Qmfoo', got '%s'", keys[0].Path().String()) + } +} + +func TestRenameSelf(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "self", "foo") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot rename key with name 'self'" { + t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) + } + } + + _, _, err = api.Key().Rename(ctx, "self", "foo", api.Key().WithForce(true)) + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot rename key with name 'self'" { + t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestRemoveSelf(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Remove(ctx, "self") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot remove key with name 'self'" { + t.Fatalf("expected error 'cannot remove key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestGenerate(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + if k.Name() != "foo" { + t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) + } + + if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { + t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) + } +} + +func TestGenerateSize(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "bar", api.Key().WithSize(1024)) + if err != nil { + t.Fatal(err) + return + } + + if k.Name() != "foo" { + t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) + } + + if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { + t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) + } +} + +func TestGenerateType(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "bar", api.Key().WithType(opts.Ed25519Key)) + if err != nil { + t.Fatal(err) + return + } + + if k.Name() != "bar" { + t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) + } + + if !strings.HasPrefix(k.Path().String(), "/ipns/Qm") { + t.Errorf("expected the key to be prefixed with '/ipns/Qm', got '%s'", k.Path().String()) + } +} + +func TestGenerateExisting(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Generate(ctx, "foo") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "key with name 'foo' already exists" { + t.Fatalf("expected error 'key with name 'foo' already exists', got '%s'", err.Error()) + } + } + + _, err = api.Key().Generate(ctx, "self") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot overwrite key with name 'self'" { + t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestList(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + l, err := api.Key().List(ctx) + if err != nil { + t.Fatal(err) + return + } + + if len(l) != 2 { + t.Fatalf("expected to get 2 keys, got %d", len(l)) + return + } + + if l[0].Name() != "self" { + t.Fatalf("expected key 0 to be called 'self', got '%s'", l[0].Name()) + return + } + + if l[1].Name() != "foo" { + t.Fatalf("expected key 1 to be called 'foo', got '%s'", l[1].Name()) + return + } + + if !strings.HasPrefix(l[0].Path().String(), "/ipns/Qm") { + t.Fatalf("expected key 0 to be prefixed with '/ipns/Qm', got '%s'", l[0].Name()) + return + } + + if !strings.HasPrefix(l[1].Path().String(), "/ipns/Qm") { + t.Fatalf("expected key 1 to be prefixed with '/ipns/Qm', got '%s'", l[1].Name()) + return + } +} + +func TestRename(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + k, overwrote, err := api.Key().Rename(ctx, "foo", "bar") + if err != nil { + t.Fatal(err) + return + } + + if overwrote { + t.Error("overwrote should be false") + } + + if k.Name() != "bar" { + t.Errorf("returned key should be called 'bar', got '%s'", k.Name()) + } +} + +func TestRenameToSelf(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "foo", "self") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot overwrite key with name 'self'" { + t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestRenameToSelfForce(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "foo", "self", api.Key().WithForce(true)) + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "cannot overwrite key with name 'self'" { + t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) + } + } +} + +func TestRenameOverwriteNoForce(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Generate(ctx, "bar") + if err != nil { + t.Fatal(err) + return + } + + _, _, err = api.Key().Rename(ctx, "foo", "bar") + if err == nil { + t.Error("expected error to not be nil") + } else { + if err.Error() != "key by that name already exists, refusing to overwrite" { + t.Fatalf("expected error 'key by that name already exists, refusing to overwrite', got '%s'", err.Error()) + } + } +} + +func TestRenameOverwrite(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + kfoo, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + _, err = api.Key().Generate(ctx, "bar") + if err != nil { + t.Fatal(err) + return + } + + k, overwrote, err := api.Key().Rename(ctx, "foo", "bar", api.Key().WithForce(true)) + if err != nil { + t.Fatal(err) + return + } + + if !overwrote { + t.Error("overwrote should be true") + } + + if k.Name() != "bar" { + t.Errorf("returned key should be called 'bar', got '%s'", k.Name()) + } + + if k.Path().String() != kfoo.Path().String() { + t.Errorf("k and kfoo should have equal paths, '%s'!='%s'", k.Path().String(), kfoo.Path().String()) + } +} + +func TestRemove(t *testing.T) { + ctx := context.Background() + _, api, err := makeAPI(ctx) + if err != nil { + t.Error(err) + } + + k, err := api.Key().Generate(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + l, err := api.Key().List(ctx) + if err != nil { + t.Fatal(err) + return + } + + if len(l) != 2 { + t.Fatalf("expected to get 2 keys, got %d", len(l)) + return + } + + p, err := api.Key().Remove(ctx, "foo") + if err != nil { + t.Fatal(err) + return + } + + if k.Path().String() != p.String() { + t.Errorf("k and p should have equal paths, '%s'!='%s'", k.Path().String(), p.String()) + } + + l, err = api.Key().List(ctx) + if err != nil { + t.Fatal(err) + return + } + + if len(l) != 1 { + t.Fatalf("expected to get 1 key, got %d", len(l)) + return + } + + if l[0].Name() != "self" { + t.Errorf("expected the key to be called 'self', got '%s'", l[0].Name()) + } +} diff --git a/core/coreapi/unixfs_test.go b/core/coreapi/unixfs_test.go index 01beccc20b43..fbcc2ebbb457 100644 --- a/core/coreapi/unixfs_test.go +++ b/core/coreapi/unixfs_test.go @@ -19,6 +19,8 @@ import ( unixfs "github.com/ipfs/go-ipfs/unixfs" cbor "gx/ipfs/QmeZv9VXw2SfVbX55LV6kGTWASKBc9ZxAVqGBeJcDGdoXy/go-ipld-cbor" + + "github.com/ipfs/go-ipfs/keystore" ) // `echo -n 'hello, world!' | ipfs add` @@ -39,6 +41,7 @@ func makeAPI(ctx context.Context) (*core.IpfsNode, coreiface.CoreAPI, error) { }, }, D: ds2.ThreadSafeCloserMapDatastore(), + K: keystore.NewMemKeystore(), } node, err := core.NewNode(ctx, &core.BuildCfg{Repo: r}) if err != nil { diff --git a/repo/mock.go b/repo/mock.go index 030c2ff28d1a..8b72d92ddfc7 100644 --- a/repo/mock.go +++ b/repo/mock.go @@ -44,7 +44,7 @@ func (m *Mock) Close() error { return errTODO } func (m *Mock) SetAPIAddr(addr ma.Multiaddr) error { return errTODO } -func (m *Mock) Keystore() keystore.Keystore { return nil } +func (m *Mock) Keystore() keystore.Keystore { return m.K } func (m *Mock) SwarmKey() ([]byte, error) { return nil, nil