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