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

import (
4
	"fmt"
5
	"time"
6

Jeromy's avatar
Jeromy committed
7
	routing "github.com/ipfs/go-ipfs/routing"
8 9
	pb "github.com/ipfs/go-ipfs/routing/dht/pb"
	record "github.com/ipfs/go-ipfs/routing/record"
10 11
	peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer"
	ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto"
Jakub Sztandera's avatar
Jakub Sztandera committed
12
	ctxfrac "gx/ipfs/QmX6DhWrpBB5NtadXmPSXYNdVvuLfJXoFNMvUMoVvP5UJa/go-context/frac"
Jeromy's avatar
Jeromy committed
13
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
14 15
)

16 17 18 19 20 21 22 23
// MaxRecordAge specifies the maximum time that any node will hold onto a record
// from the time its received. This does not apply to any other forms of validity that
// the record may contain.
// For example, a record may contain an ipns entry with an EOL saying its valid
// until the year 2020 (a great time in the future). For that record to stick around
// it must be rebroadcasted more frequently than once every 'MaxRecordAge'
const MaxRecordAge = time.Hour * 36

24
func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) {
25 26 27 28 29 30 31 32 33
	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.
34
	ctxT, cancelFunc := ctxfrac.WithDeadlineFraction(ctx, 0.3)
Henry's avatar
Henry committed
35
	defer cancelFunc()
36
	if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil {
Jeromy's avatar
Jeromy committed
37 38 39 40
		err := dht.peerstore.AddPubKey(p, pk)
		if err != nil {
			return pk, err
		}
41
		return pk, nil
Jeromy's avatar
Jeromy committed
42 43
	}

44
	// last ditch effort: let's try the dht.
Richard Littauer's avatar
Richard Littauer committed
45
	log.Debugf("pk for %s not in peerstore, and peer failed. Trying DHT.", p)
Jeromy's avatar
Jeromy committed
46
	pkkey := routing.KeyForPublicKey(p)
47 48

	val, err := dht.GetValue(ctxT, pkkey)
Jeromy's avatar
Jeromy committed
49 50 51 52 53
	if err != nil {
		log.Warning("Failed to find requested public key.")
		return nil, err
	}

54
	pk, err = ci.UnmarshalPublicKey(val)
Jeromy's avatar
Jeromy committed
55
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56
		log.Debugf("Failed to unmarshal public key: %s", err)
Jeromy's avatar
Jeromy committed
57 58
		return nil, err
	}
Jeromy's avatar
Jeromy committed
59 60

	return pk, dht.peerstore.AddPubKey(p, pk)
Jeromy's avatar
Jeromy committed
61 62
}

63 64 65 66 67 68 69 70
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
	}

Jeromy's avatar
Jeromy committed
71
	pkkey := routing.KeyForPublicKey(p)
72 73 74 75 76 77 78 79
	pmes, err := dht.getValueSingle(ctx, p, pkkey)
	if err != nil {
		return nil, err
	}

	// node doesn't have key :(
	record := pmes.GetRecord()
	if record == nil {
Richard Littauer's avatar
Richard Littauer committed
80
		return nil, fmt.Errorf("Node not responding with its public key: %s", p)
81 82 83 84 85 86
	}

	// 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()
87
	log.Debug("DHT got a value from other peer")
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

	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!
Richard Littauer's avatar
Richard Littauer committed
103
	log.Debugf("DHT got public key from node itself.")
104 105 106 107 108 109 110
	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 {

Jeromy's avatar
Jeromy committed
111 112 113 114 115 116 117 118 119 120 121
	if len(r.Signature) > 0 {
		// First, validate the signature
		p := peer.ID(r.GetAuthor())
		pk := dht.peerstore.PubKey(p)
		if pk == nil {
			return fmt.Errorf("do not have public key for %s", p)
		}

		if err := record.CheckRecordSig(r, pk); err != nil {
			return err
		}
122 123
	}

Jeromy's avatar
Jeromy committed
124
	return dht.Validator.VerifyRecord(r)
125 126 127 128 129 130 131 132 133
}

// 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 {

Jeromy's avatar
Jeromy committed
134 135 136
	if len(r.Signature) > 0 {
		// get the public key, search for it if necessary.
		p := peer.ID(r.GetAuthor())
137
		pk, err := dht.GetPublicKey(ctx, p)
Jeromy's avatar
Jeromy committed
138 139 140 141 142 143 144 145
		if err != nil {
			return err
		}

		err = record.CheckRecordSig(r, pk)
		if err != nil {
			return err
		}
146 147
	}

Jeromy's avatar
Jeromy committed
148
	return dht.Validator.VerifyRecord(r)
149
}