diff --git a/config/config.go b/config/config.go index f1907edf1f7..779146057c0 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,7 @@ import ( // Identity tracks the configuration of the local node's identity. type Identity struct { - PeerID string + Curve uint16 } // Datastore tracks the configuration of the datastore. @@ -24,7 +24,9 @@ type Config struct { var defaultConfigFilePath = "~/.go-ipfs/config" var defaultConfigFile = `{ - "identity": {}, + "identity": { + "curve": 224 + }, "datastore": { "type": "leveldb", "path": "~/.go-ipfs/datastore" diff --git a/core/core.go b/core/core.go index 66058cf61c2..85d62a07fa5 100644 --- a/core/core.go +++ b/core/core.go @@ -53,6 +53,11 @@ func NewIpfsNode(cfg *config.Config) (*IpfsNode, error) { return nil, fmt.Errorf("configuration required") } + id, err := peer.NewIdentity(cfg.Identity) + if err != nil { + return nil, err + } + d, err := makeDatastore(cfg.Datastore) if err != nil { return nil, err @@ -67,6 +72,7 @@ func NewIpfsNode(cfg *config.Config) (*IpfsNode, error) { n := &IpfsNode{ Config: cfg, + Identity: id, PeerMap: &peer.Map{}, Datastore: d, Blocks: bs, diff --git a/namesys/namesys.go b/namesys/namesys.go new file mode 100644 index 00000000000..85a70aac789 --- /dev/null +++ b/namesys/namesys.go @@ -0,0 +1,46 @@ +package namesys + +import ( + "errors" + proquint "github.com/Bren2010/proquint" + mh "github.com/jbenet/go-multihash" + "net" + "regexp" + "strings" +) + +func Resolve(name string) (mh.Multihash, error) { + b58Exp := "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]*$" + pqExp := "^([abdfghijklmnoprstuvz]{5}-)*[abdfghijklmnoprstuvz]{5}$" + + isB58, err := regexp.MatchString(b58Exp, name) + if err != nil { + return nil, err + } + + isPQ, err := regexp.MatchString(pqExp, name) + if err != nil { + return nil, err + } + + if isB58 { // Is a base58 hash. + return mh.FromB58String(name) + } else if isPQ { // Is a Proquint identifier. + return mh.Multihash(proquint.Decode(name)), nil + } else { // Is a domain name. Hopefully. + txts, err := net.LookupTXT(name) + if err != nil { + return nil, err + } + + for i := 0; i < len(txts); i++ { + var parts []string = strings.SplitN(txts[i], "=", 2) + + if len(parts) == 2 && parts[0] == "ipfs" { + return mh.FromB58String(parts[1]) + } + } + + return nil, errors.New("Could not resolve IPNS.") + } +} diff --git a/path/path.go b/path/path.go index 3f1c3997e4c..ddcbeedb1b7 100644 --- a/path/path.go +++ b/path/path.go @@ -1,10 +1,10 @@ package path import ( + namesys "github.com/jbenet/go-ipfs/namesys" "fmt" merkledag "github.com/jbenet/go-ipfs/merkledag" u "github.com/jbenet/go-ipfs/util" - mh "github.com/jbenet/go-multihash" "path" "strings" ) @@ -33,8 +33,7 @@ func (s *Resolver) ResolvePath(fpath string) (*merkledag.Node, error) { return nil, fmt.Errorf("ipfs path must contain at least one component") } - // first element in the path is a b58 hash (for now) - h, err := mh.FromB58String(parts[0]) + h, err := name.Resolve(parts[0]) if err != nil { return nil, err } diff --git a/peer/peer.go b/peer/peer.go index e7c3af2b46d..52838c24406 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -1,11 +1,19 @@ package peer import ( + config "github.com/jbenet/go-ipfs/config" u "github.com/jbenet/go-ipfs/util" ma "github.com/jbenet/go-multiaddr" mh "github.com/jbenet/go-multihash" "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha1" + "crypto/sha256" + "errors" + "math/big" ) // ID is a byte slice representing the identity of a peer. @@ -23,7 +31,12 @@ type Map map[u.Key]*Peer // ID, and relevant Addresses. type Peer struct { ID ID + PubKey []byte Addresses []*ma.Multiaddr + + curve elliptic.Curve + hellman []byte + signing *ecdsa.PrivateKey } // Key returns the ID as a Key (string) for maps. @@ -52,3 +65,146 @@ func (p *Peer) NetAddress(n string) *ma.Multiaddr { } return nil } + +// Generates a shared secret key between us and the given public key. +func (p *Peer) Secret(pubKey []byte) ([]byte, error) { + // Verify and unpack node's public key. + curveSize := p.curve.Params().BitSize + + if len(pubKey) != (curveSize / 2) { + return nil, errors.New("Malformed public key.") + } + + bound := (curveSize / 8) + x := big.NewInt(0) + y := big.NewInt(0) + + x.SetBytes(pubKey[0:bound]) + y.SetBytes(pubKey[bound : bound*2]) + + if !p.curve.IsOnCurve(x, y) { + return nil, errors.New("Invalid public key.") + } + + // Generate shared secret. + secret, _ := p.curve.ScalarMult(x, y, p.hellman) + + return secret.Bytes(), nil +} + +// Signs a given piece of data. +func (p *Peer) Sign(data []byte) ([]byte, error) { + var out bytes.Buffer + + hash := sha256.New() + hash.Write(data) + + r, s, err := ecdsa.Sign(rand.Reader, p.signing, hash.Sum(nil)) + if err != nil { + return nil, err + } + + out.Write(r.Bytes()) + out.Write(s.Bytes()) + + return out.Bytes(), nil +} + +// Verifies a signature on a given piece of data. +func (p *Peer) Verify(pubKey, data, sig []byte) (bool, error) { + curveSize := p.curve.Params().BitSize + + // Verify and unpack public key. + if len(pubKey) != (curveSize / 2) { + return false, errors.New("Malformed public key.") + } + + bound1 := (curveSize / 8) + x := big.NewInt(0) + y := big.NewInt(0) + + x.SetBytes(pubKey[bound1*2 : bound1*3]) + y.SetBytes(pubKey[bound1*3:]) + + parsedPubKey := &ecdsa.PublicKey{p.curve, x, y} + + // Verify and unpack signature. + if len(sig) != (curveSize / 4) { + return false, errors.New("Malformed signature.") + } + + bound2 := (curveSize / 8) + r := big.NewInt(0) + s := big.NewInt(0) + + r.SetBytes(sig[0:bound2]) + s.SetBytes(sig[bound2:]) + + // Verify signature. + hash := sha256.New() + hash.Write(data) + + ok := ecdsa.Verify(parsedPubKey, hash.Sum(nil), r, s) + + return ok, nil +} + +/** +* Generates a new, random peer identity. +* +* @return {Peer} + */ +func NewIdentity(cfg *config.Identity) (*Peer, error) { + // Select curve given in config. + var curve elliptic.Curve + var pubKey bytes.Buffer + + switch cfg.Curve { + case 224: + curve = elliptic.P224() + case 256: + curve = elliptic.P256() + case 384: + curve = elliptic.P384() + case 521: + curve = elliptic.P521() + default: + return nil, errors.New("Bad curve name in config.") + } + + // Generate a random ECDH keypair. + priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, err + } + + pubKey.Write(x.Bytes()) + pubKey.Write(y.Bytes()) + + // Generate a random ECDSA keypair. + signingPriv, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, err + } + + pubKey.Write(signingPriv.PublicKey.X.Bytes()) + pubKey.Write(signingPriv.PublicKey.Y.Bytes()) + + // Generate peer ID + hash := sha1.New() // THIS NEEDS TO BE SHA256 + hash.Write(pubKey.Bytes()) + + id, err := mh.EncodeName(hash.Sum(nil), "sha1") + if err != nil { + return nil, err + } + + return &Peer{ + id, + pubKey.Bytes(), + []*ma.Multiaddr{}, + curve, + priv, + signingPriv, + }, nil +}