From aa5a34a6d175777cdb61c3d81e8e02753fd4da0b Mon Sep 17 00:00:00 2001
From: Jeromy <jeromyj@gmail.com>
Date: Tue, 2 Sep 2014 12:58:35 -0700
Subject: [PATCH] add init command to generate crypto keys

---
 cmd/ipfs/init.go     | 81 ++++++++++++++++++++++++++++++++++++++++++++
 cmd/ipfs/ipfs.go     |  1 +
 config/config.go     | 29 +++++++++++-----
 core/core.go         | 28 +++++++++++----
 identify/identify.go | 12 +++++++
 5 files changed, 137 insertions(+), 14 deletions(-)
 create mode 100644 cmd/ipfs/init.go

diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go
new file mode 100644
index 000000000..177e415eb
--- /dev/null
+++ b/cmd/ipfs/init.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+	"encoding/base64"
+	"errors"
+	"os"
+
+	"github.com/gonuts/flag"
+	"github.com/jbenet/commander"
+	config "github.com/jbenet/go-ipfs/config"
+	"github.com/jbenet/go-ipfs/identify"
+	u "github.com/jbenet/go-ipfs/util"
+)
+
+var cmdIpfsInit = &commander.Command{
+	UsageLine: "init",
+	Short:     "Initialize ipfs local configuration",
+	Long: `ipfs init
+
+	Initializes ipfs configuration files and generates a
+	new keypair.
+`,
+	Run:  initCmd,
+	Flag: *flag.NewFlagSet("ipfs-init", flag.ExitOnError),
+}
+
+func init() {
+	cmdIpfsInit.Flag.Int("b", 4096, "number of bits for keypair")
+	cmdIpfsInit.Flag.String("p", "", "passphrase for encrypting keys")
+	cmdIpfsInit.Flag.Bool("f", false, "force overwrite of existing config")
+}
+
+func initCmd(c *commander.Command, inp []string) error {
+	_, err := os.Lstat(config.DefaultConfigFilePath)
+	force := c.Flag.Lookup("f").Value.Get().(bool)
+	if err != nil && !force {
+		return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)")
+	}
+	cfg := new(config.Config)
+
+	cfg.Datastore = new(config.Datastore)
+	dspath, err := u.TildeExpansion("~/.go-ipfs/datastore")
+	if err != nil {
+		return err
+	}
+	cfg.Datastore.Path = dspath
+	cfg.Datastore.Type = "leveldb"
+
+	cfg.Identity = new(config.Identity)
+	// This needs thought
+	// cfg.Identity.Address = ""
+
+	nbits := c.Flag.Lookup("b").Value.Get().(int)
+	if nbits < 1024 {
+		return errors.New("Bitsize less than 1024 is considered unsafe.")
+	}
+	kp, err := identify.GenKeypair(nbits)
+	if err != nil {
+		return err
+	}
+
+	// pretend to encrypt key, then store it unencrypted
+	enckey := base64.StdEncoding.EncodeToString(kp.PrivBytes())
+	cfg.Identity.PrivKey = enckey
+
+	id, err := kp.ID()
+	if err != nil {
+		return err
+	}
+	cfg.Identity.PeerID = id.Pretty()
+
+	path, err := u.TildeExpansion(config.DefaultConfigFilePath)
+	if err != nil {
+		return err
+	}
+	err = config.WriteConfigFile(path, cfg)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go
index e7a955d51..d492298e3 100644
--- a/cmd/ipfs/ipfs.go
+++ b/cmd/ipfs/ipfs.go
@@ -46,6 +46,7 @@ Use "ipfs help <command>" for more information about a command.
 		cmdIpfsVersion,
 		cmdIpfsCommands,
 		cmdIpfsMount,
+		cmdIpfsInit,
 	},
 	Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
 }
diff --git a/config/config.go b/config/config.go
index 2bbbae3d7..b80ff1a2c 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,6 +1,10 @@
 package config
 
 import (
+	"crypto"
+	"crypto/x509"
+	"encoding/base64"
+	"errors"
 	"os"
 
 	u "github.com/jbenet/go-ipfs/util"
@@ -9,6 +13,7 @@ import (
 // Identity tracks the configuration of the local node's identity.
 type Identity struct {
 	PeerID  string
+	PrivKey string
 	Address string
 }
 
@@ -29,8 +34,8 @@ type Config struct {
 	Peers     []*SavedPeer
 }
 
-var defaultConfigFilePath = "~/.go-ipfs/config"
-var defaultConfigFile = `{
+var DefaultConfigFilePath = "~/.go-ipfs/config"
+var DefaultConfigFile = `{
   "identity": {},
   "datastore": {
     "type": "leveldb",
@@ -39,10 +44,20 @@ var defaultConfigFile = `{
 }
 `
 
+func (i *Identity) DecodePrivateKey(passphrase string) (crypto.PrivateKey, error) {
+	pkb, err := base64.StdEncoding.DecodeString(i.PrivKey)
+	if err != nil {
+		return nil, err
+	}
+
+	//pretend to actually decrypt private key
+	return x509.ParsePKCS1PrivateKey(pkb)
+}
+
 // Filename returns the proper tilde expanded config filename.
 func Filename(filename string) (string, error) {
 	if len(filename) == 0 {
-		filename = defaultConfigFilePath
+		filename = DefaultConfigFilePath
 	}
 
 	// tilde expansion on config file
@@ -56,11 +71,9 @@ func Load(filename string) (*Config, error) {
 		return nil, err
 	}
 
-	// if nothing is there, write first config file.
+	// if nothing is there, fail. User must run 'ipfs init'
 	if _, err := os.Stat(filename); os.IsNotExist(err) {
-		if err := WriteFile(filename, []byte(defaultConfigFile)); err != nil {
-			return nil, err
-		}
+		return nil, errors.New("ipfs not initialized, please run 'ipfs init'")
 	}
 
 	var cfg Config
@@ -80,5 +93,5 @@ func Load(filename string) (*Config, error) {
 
 // Set sets the value of a particular config key
 func Set(filename, key, value string) error {
-	return nil
+	return WriteConfigKey(filename, key, value)
 }
diff --git a/core/core.go b/core/core.go
index 2b32aafa9..1c3e06963 100644
--- a/core/core.go
+++ b/core/core.go
@@ -1,12 +1,17 @@
 package core
 
 import (
+	"crypto"
+	"crypto/rsa"
+	"errors"
 	"fmt"
 
 	ds "github.com/jbenet/datastore.go"
+	b58 "github.com/jbenet/go-base58"
 	"github.com/jbenet/go-ipfs/bitswap"
 	bserv "github.com/jbenet/go-ipfs/blockservice"
 	config "github.com/jbenet/go-ipfs/config"
+	"github.com/jbenet/go-ipfs/identify"
 	merkledag "github.com/jbenet/go-ipfs/merkledag"
 	path "github.com/jbenet/go-ipfs/path"
 	peer "github.com/jbenet/go-ipfs/peer"
@@ -98,17 +103,28 @@ func loadBitswap(cfg *config.Config, d ds.Datastore) (*bitswap.BitSwap, error) {
 		return nil, err
 	}
 
+	pk, err := cfg.Identity.DecodePrivateKey("")
+	if err != nil {
+		return nil, err
+	}
+
+	var pubkey crypto.PublicKey
+	switch k := pk.(type) {
+	case *rsa.PrivateKey:
+		pubkey = &k.PublicKey
+	default:
+		return nil, identify.ErrUnsupportedKeyType
+	}
+
 	local := &peer.Peer{
-		ID:        peer.ID(cfg.Identity.PeerID),
+		ID:        peer.ID(b58.Decode(cfg.Identity.PeerID)),
 		Addresses: []*ma.Multiaddr{maddr},
+		PrivKey:   pk,
+		PubKey:    pubkey,
 	}
 
 	if len(local.ID) == 0 {
-		mh, err := u.Hash([]byte("blah blah blah ID"))
-		if err != nil {
-			return nil, err
-		}
-		local.ID = peer.ID(mh)
+		return nil, errors.New("No peer ID in config! (was ipfs init run?)")
 	}
 
 	net := swarm.NewSwarm(local)
diff --git a/identify/identify.go b/identify/identify.go
index e19b53ce7..43b0f0a08 100644
--- a/identify/identify.go
+++ b/identify/identify.go
@@ -16,6 +16,9 @@ import (
 	u "github.com/jbenet/go-ipfs/util"
 )
 
+// ErrUnsupportedKeyType is returned when a private key cast/type switch fails.
+var ErrUnsupportedKeyType = errors.New("unsupported key type")
+
 // Perform initial communication with this peer to share node ID's and
 // initiate communication
 func Handshake(self, remote *peer.Peer, in, out chan []byte) error {
@@ -151,6 +154,15 @@ func (pk *KeyPair) ID() (peer.ID, error) {
 	return peer.ID(hash), nil
 }
 
+func (pk *KeyPair) PrivBytes() []byte {
+	switch k := pk.Priv.(type) {
+	case *rsa.PrivateKey:
+		return x509.MarshalPKCS1PrivateKey(k)
+	default:
+		panic("Unsupported private key type.")
+	}
+}
+
 func (kp *KeyPair) Save(dir string) error {
 	switch k := kp.Priv.(type) {
 	case *rsa.PrivateKey:
-- 
GitLab