records.go 3.42 KB
Newer Older
1 2 3
package dht

import (
4
	"fmt"
5

Jeromy's avatar
Jeromy committed
6
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
7

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
	ci "github.com/jbenet/go-ipfs/p2p/crypto"
9
	peer "github.com/jbenet/go-ipfs/p2p/peer"
10 11
	pb "github.com/jbenet/go-ipfs/routing/dht/pb"
	u "github.com/jbenet/go-ipfs/util"
12
	ctxutil "github.com/jbenet/go-ipfs/util/ctx"
13 14
)

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// KeyForPublicKey returns the key used to retrieve public keys
// from the dht.
func KeyForPublicKey(id peer.ID) u.Key {
	return u.Key("/pk/" + string(id))
}

func (dht *IpfsDHT) getPublicKeyOnline(ctx context.Context, p peer.ID) (ci.PubKey, error) {
	log.Debugf("getPublicKey for: %s", p)

	// check locally.
	pk := dht.peerstore.PubKey(p)
	if pk != nil {
		return pk, nil
	}

	// ok, try the node itself. if they're overwhelmed or slow we can move on.
	ctxT, _ := ctxutil.WithDeadlineFraction(ctx, 0.3)
	if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil {
		return pk, nil
Jeromy's avatar
Jeromy committed
34 35
	}

36 37 38 39 40 41
	// last ditch effort: let's try the dht.
	log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p)
	pkkey := KeyForPublicKey(p)

	// ok, try the node itself. if they're overwhelmed or slow we can move on.
	val, err := dht.GetValue(ctxT, pkkey)
Jeromy's avatar
Jeromy committed
42 43 44 45 46
	if err != nil {
		log.Warning("Failed to find requested public key.")
		return nil, err
	}

47
	pk, err = ci.UnmarshalPublicKey(val)
Jeromy's avatar
Jeromy committed
48 49 50 51
	if err != nil {
		log.Errorf("Failed to unmarshal public key: %s", err)
		return nil, err
	}
52
	return pk, nil
Jeromy's avatar
Jeromy committed
53 54
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.PubKey, error) {

	// check locally, just in case...
	pk := dht.peerstore.PubKey(p)
	if pk != nil {
		return pk, nil
	}

	pkkey := KeyForPublicKey(p)
	pmes, err := dht.getValueSingle(ctx, p, pkkey)
	if err != nil {
		return nil, err
	}

	// node doesn't have key :(
	record := pmes.GetRecord()
	if record == nil {
		return nil, fmt.Errorf("node not responding with its public key: %s", p)
	}

	// Success! We were given the value. we don't need to check
	// validity because a) we can't. b) we know the hash of the
	// key we're looking for.
	val := record.GetValue()
	log.Debug("dht got a value from other peer.")

	pk, err = ci.UnmarshalPublicKey(val)
	if err != nil {
		return nil, err
	}

	id, err := peer.IDFromPublicKey(pk)
	if err != nil {
		return nil, err
	}
	if id != p {
		return nil, fmt.Errorf("public key does not match id: %s", p)
	}

	// ok! it's valid. we got it!
	log.Debugf("dht got public key from node itself.")
	return pk, nil
}

// verifyRecordLocally attempts to verify a record. if we do not have the public
// key, we fail. we do not search the dht.
func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error {

103
	// First, validate the signature
104 105 106 107 108 109
	p := peer.ID(r.GetAuthor())
	pk := dht.peerstore.PubKey(p)
	if pk == nil {
		return fmt.Errorf("do not have public key for %s", p)
	}

110
	return dht.Validator.VerifyRecord(r, pk)
111 112 113 114 115 116 117 118 119 120 121 122
}

// verifyRecordOnline verifies a record, searching the DHT for the public key
// if necessary. The reason there is a distinction in the functions is that
// retrieving arbitrary public keys from the DHT as a result of passively
// receiving records (e.g. through a PUT_VALUE or ADD_PROVIDER) can cause a
// massive amplification attack on the dht. Use with care.
func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error {

	// get the public key, search for it if necessary.
	p := peer.ID(r.GetAuthor())
	pk, err := dht.getPublicKeyOnline(ctx, p)
123 124 125 126
	if err != nil {
		return err
	}

127
	return dht.Validator.VerifyRecord(r, pk)
128
}