From bdb9e4e21a11a9c21787fa91a96ff49335a07a35 Mon Sep 17 00:00:00 2001
From: Steven Allen <steven@stebalien.com>
Date: Fri, 8 Mar 2019 18:47:02 -0800
Subject: [PATCH] pluggable keystores

This patch allows keystores to be configured via the config and plugins.

It's completely untested for now, but I started this so I figured I'd get it to
a point where we could consider finishing it.

License: MIT
Signed-off-by: Steven Allen <steven@stebalien.com>
---
 go.mod                   |  7 ++++--
 go.sum                   | 12 ++++++++++
 plugin/keystore.go       | 24 +++++++++++++++++++
 plugin/loader/loader.go  | 10 ++++++++
 repo/fsrepo/fsrepo.go    | 19 +++++++++++++--
 repo/fsrepo/keystores.go | 52 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 120 insertions(+), 4 deletions(-)
 create mode 100644 plugin/keystore.go
 create mode 100644 repo/fsrepo/keystores.go

diff --git a/go.mod b/go.mod
index 06f13475b..5cc68e16d 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	github.com/cenkalti/backoff v2.1.1+incompatible
 	github.com/dustin/go-humanize v1.0.0
 	github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302
+	github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
 	github.com/fatih/color v1.7.0 // indirect
 	github.com/fsnotify/fsnotify v1.4.7
 	github.com/gogo/protobuf v1.2.1
@@ -33,7 +34,7 @@ require (
 	github.com/ipfs/go-ipfs-chunker v0.0.1
 	github.com/ipfs/go-ipfs-cmdkit v0.0.1
 	github.com/ipfs/go-ipfs-cmds v0.0.1
-	github.com/ipfs/go-ipfs-config v0.0.1
+	github.com/ipfs/go-ipfs-config v0.0.0-20190309024316-a0bdc3f74854
 	github.com/ipfs/go-ipfs-ds-help v0.0.1
 	github.com/ipfs/go-ipfs-exchange-interface v0.0.1
 	github.com/ipfs/go-ipfs-exchange-offline v0.0.1
@@ -51,12 +52,12 @@ require (
 	github.com/ipfs/go-metrics-prometheus v0.0.1
 	github.com/ipfs/go-mfs v0.0.1
 	github.com/ipfs/go-path v0.0.1
+	github.com/ipfs/go-prompt v0.0.1
 	github.com/ipfs/go-unixfs v0.0.1
 	github.com/ipfs/go-verifcid v0.0.1
 	github.com/ipfs/hang-fds v0.0.1
 	github.com/ipfs/interface-go-ipfs-core v0.0.2
 	github.com/ipfs/iptb v1.4.0
-	github.com/ipfs/iptb-plugins v0.0.1
 	github.com/jbenet/go-is-domain v0.0.0-20160119110217-ba9815c809e0
 	github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c
 	github.com/jbenet/go-random-files v0.0.0-20190219210431-31b3f20ebded
@@ -67,6 +68,7 @@ require (
 	github.com/libp2p/go-libp2p-circuit v0.0.1
 	github.com/libp2p/go-libp2p-connmgr v0.0.1
 	github.com/libp2p/go-libp2p-crypto v0.0.1
+	github.com/libp2p/go-libp2p-daemon v0.0.1 // indirect
 	github.com/libp2p/go-libp2p-host v0.0.1
 	github.com/libp2p/go-libp2p-interface-connmgr v0.0.1
 	github.com/libp2p/go-libp2p-kad-dht v0.0.3
@@ -102,6 +104,7 @@ require (
 	github.com/prometheus/client_golang v0.9.2
 	github.com/syndtr/goleveldb v1.0.0
 	github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc
+	github.com/whyrusleeping/go-ctrlnet v0.0.0-20180313164037-f564fbbdaa95 // indirect
 	github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible
 	github.com/whyrusleeping/go-smux-yamux v2.0.8+incompatible
 	github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1
diff --git a/go.sum b/go.sum
index b87b29cfd..1dafe78cb 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,8 @@ github.com/Kubuxu/go-os-helper v0.0.1 h1:EJiD2VUQyh5A9hWJLmc6iWg6yIcJ7jpBcwC8GMG
 github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
 github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd h1:HNhzThEtZW714v8Eda8sWWRcu9WSzJC+oCyjRjvZgRA=
 github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd/go.mod h1:bqoB8kInrTeEtYAwaIXoSRqdwnjQmFhsfusnzyui6yY=
+github.com/Songmu/prompter v0.0.0-20181014095714-d227c68538bd h1:WPP3dYxBYZBo0q3t14UIvD0Myr848agWCVSlScH17E0=
+github.com/Songmu/prompter v0.0.0-20181014095714-d227c68538bd/go.mod h1:fNhSFBGC+sg+dZ7AqDHgq+xYiom23TeTESzUbO7PIrE=
 github.com/Stebalien/go-bitfield v0.0.0-20180330043415-076a62f9ce6e h1:2Z+EBRrOJsA3psnUPcEWMIH2EIga1xHflQcr/EZslx8=
 github.com/Stebalien/go-bitfield v0.0.0-20180330043415-076a62f9ce6e/go.mod h1:3oM7gXIttpYDAJXpVNnSCiUMYBLIZ6cb1t+Ip982MRo=
 github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
@@ -139,6 +141,8 @@ github.com/ipfs/go-ipfs-cmdkit v0.0.1 h1:X6YXEAjUljTzevE6DPUKXSqcgf+4FXzcn5B957F
 github.com/ipfs/go-ipfs-cmdkit v0.0.1/go.mod h1:9FtbMdUabcSqv/G4/8WCxSLxkZxn/aZEFrxxqnVcRbg=
 github.com/ipfs/go-ipfs-cmds v0.0.1 h1:wPTynLMa+JImcTsPaVmrUDP8mJ3S8HQVUWixnKi7+k4=
 github.com/ipfs/go-ipfs-cmds v0.0.1/go.mod h1:k7I8PptE2kCJchR3ta546LRyxl4/uBYbLQHOJM0sUQ8=
+github.com/ipfs/go-ipfs-config v0.0.0-20190309024316-a0bdc3f74854 h1:/egI93XR8dai8cn+W2epdY0rCppfFsGjt8UQ741Qhy8=
+github.com/ipfs/go-ipfs-config v0.0.0-20190309024316-a0bdc3f74854/go.mod h1:yqJG+KMcBiqX2v2EhxeTi30T0lLGZx6uY3f5B3gD4HI=
 github.com/ipfs/go-ipfs-config v0.0.1 h1:6ED08emzI1imdsAjixFi2pEyZxTVD5ECKtCOxLBx+Uc=
 github.com/ipfs/go-ipfs-config v0.0.1/go.mod h1:KDbHjNyg4e6LLQSQpkgQMBz6Jf4LXiWAcmnkcwmH0DU=
 github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
@@ -182,6 +186,10 @@ github.com/ipfs/go-mfs v0.0.1 h1:EcQeaxW2DO3LmRXEtbWCGxhZ7YmMR+X7nzjYChBzG8s=
 github.com/ipfs/go-mfs v0.0.1/go.mod h1:rUT0dKNWkKa1T+MobpBL2zANn7p8Y6unXANC0PV2FLk=
 github.com/ipfs/go-path v0.0.1 h1:6UskTq8xYVs3zVnHjXDvoCqw22dKWK1BwD1cy1cuHyc=
 github.com/ipfs/go-path v0.0.1/go.mod h1:ztzG4iSBN2/CJa93rtHAv/I+mpK+BGALeUoJzhclhw0=
+github.com/ipfs/go-prompt v0.0.0-20190309021543-60746b51630e h1:rClV9cZC54BQr25YltQRNc3a1GSxiCZSF7jwEWltFVU=
+github.com/ipfs/go-prompt v0.0.0-20190309021543-60746b51630e/go.mod h1:9sBllkC9Vc4oYBP/S8iR8IRPbyVSwottiVgKsKA1atE=
+github.com/ipfs/go-prompt v0.0.1 h1:yWWyaH5YKSkU9Gejgg8d41wzh5onvckStxK6CuLGSEo=
+github.com/ipfs/go-prompt v0.0.1/go.mod h1:9sBllkC9Vc4oYBP/S8iR8IRPbyVSwottiVgKsKA1atE=
 github.com/ipfs/go-todocounter v0.0.1 h1:kITWA5ZcQZfrUnDNkRn04Xzh0YFaDFXsoO2A81Eb6Lw=
 github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4=
 github.com/ipfs/go-unixfs v0.0.1 h1:CTTGqLxU5+PRkkeA+w1peStqRWFD1Kya+yZgIT4Xy1w=
@@ -342,6 +350,8 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
+github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@@ -461,6 +471,8 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU=
 golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/plugin/keystore.go b/plugin/keystore.go
new file mode 100644
index 000000000..db496bc43
--- /dev/null
+++ b/plugin/keystore.go
@@ -0,0 +1,24 @@
+package plugin
+
+import (
+	keystore "github.com/ipfs/go-ipfs/keystore"
+	prompt "github.com/ipfs/go-prompt"
+)
+
+// PluginKeystore is an interface that can be implemented to new keystore
+// backends.
+type PluginKeystore interface {
+	Plugin
+
+	// KeystoreTypeName returns the the keystore's type. In addition to
+	// loading the keystore plugin, the user must configure their go-ipfs
+	// node to use the specified keystore backend.
+	KeystoreTypeName() string
+
+	// Open opens the keystore. Prompter may be nil if non-interactive.
+	Open(
+		repoPath string,
+		config map[string]interface{},
+		prompter prompt.Prompter,
+	) (keystore.Keystore, error)
+}
diff --git a/plugin/loader/loader.go b/plugin/loader/loader.go
index 939edb9ad..6085b20b1 100644
--- a/plugin/loader/loader.go
+++ b/plugin/loader/loader.go
@@ -105,6 +105,12 @@ func (loader *PluginLoader) Inject() error {
 				return err
 			}
 		}
+		if pl, ok := pl.(plugin.PluginKeystore); ok {
+			err := injectKeystorePlugin(pl)
+			if err != nil {
+				return err
+			}
+		}
 	}
 	return nil
 }
@@ -160,6 +166,10 @@ func injectIPLDPlugin(pl plugin.PluginIPLD) error {
 	return pl.RegisterInputEncParsers(coredag.DefaultInputEncParsers)
 }
 
+func injectKeystorePlugin(pl plugin.PluginKeystore) error {
+	return fsrepo.AddKeystore(pl.KeystoreTypeName(), pl.Open)
+}
+
 func injectTracerPlugin(pl plugin.PluginTracer) error {
 	tracer, err := pl.InitTracer()
 	if err != nil {
diff --git a/repo/fsrepo/fsrepo.go b/repo/fsrepo/fsrepo.go
index d35c97140..3524cbf8d 100644
--- a/repo/fsrepo/fsrepo.go
+++ b/repo/fsrepo/fsrepo.go
@@ -384,8 +384,23 @@ func (r *FSRepo) openConfig() error {
 }
 
 func (r *FSRepo) openKeystore() error {
-	ksp := filepath.Join(r.path, "keystore")
-	ks, err := keystore.NewFSKeystore(ksp)
+	spec := r.config.Keystore
+	if spec == nil {
+		spec = map[string]interface{}{"type": "files", "path": "keystore"}
+	}
+
+	ksType, ok := spec["type"]
+	if !ok {
+		return fmt.Errorf("keystore config lacks a type")
+	}
+
+	ksCtor, ok := keystores[ksType]
+	if !ok {
+		return fmt.Errorf("couldn't find keystore of type %q", ksType)
+	}
+
+	// TODO: feed through a prompter.
+	ks, err := ksCtor(r.path, spec, nil)
 	if err != nil {
 		return err
 	}
diff --git a/repo/fsrepo/keystores.go b/repo/fsrepo/keystores.go
new file mode 100644
index 000000000..65de1d537
--- /dev/null
+++ b/repo/fsrepo/keystores.go
@@ -0,0 +1,52 @@
+package fsrepo
+
+import (
+	"fmt"
+	"path/filepath"
+
+	keystore "github.com/ipfs/go-ipfs/keystore"
+
+	prompt "github.com/ipfs/go-prompt"
+)
+
+type KeystoreConstructor func(
+	repoPath string,
+	cfg map[string]interface{},
+	prompter prompt.Prompter,
+) (keystore.Keystore, error)
+
+var keystores = map[string]KeystoreConstructor{
+	"memory": MemKeystoreFromConfig,
+	"files":  FSKeystoreFromConfig,
+}
+
+func AddKeystore(name string, ctor KeystoreConstructor) error {
+	_, ok := keystores[name]
+	if ok {
+		return fmt.Errorf("keystore %q registered more than once", name)
+	}
+	keystores[name] = ctor
+	return nil
+}
+
+func FSKeystoreFromConfig(
+	repo string,
+	cfg map[string]interface{},
+	_ prompt.Prompter,
+) (keystore.Keystore, error) {
+	path, ok := cfg["path"].(string)
+	if !ok {
+		return nil, fmt.Errorf("'path' field is missing or not a string")
+	}
+	return keystore.NewFSKeystore(filepath.Join(repo, path))
+}
+
+// MemKeystoreFromConfig opens an in-memory keystore based on the current
+// config.
+func MemKeystoreFromConfig(
+	_ string,
+	_ map[string]interface{},
+	_ prompt.Prompter,
+) (keystore.Keystore, error) {
+	return keystore.NewMemKeystore(), nil
+}
-- 
GitLab