-
Notifications
You must be signed in to change notification settings - Fork 0
/
records.go
140 lines (115 loc) · 3.62 KB
/
records.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package dht
import (
"fmt"
ctxfrac "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-context/frac"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
ci "github.com/ipfs/go-ipfs/p2p/crypto"
peer "github.com/ipfs/go-ipfs/p2p/peer"
routing "github.com/ipfs/go-ipfs/routing"
pb "github.com/ipfs/go-ipfs/routing/dht/pb"
record "github.com/ipfs/go-ipfs/routing/record"
)
func (dht *IpfsDHT) GetPublicKey(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, cancelFunc := ctxfrac.WithDeadlineFraction(ctx, 0.3)
defer cancelFunc()
if pk, err := dht.getPublicKeyFromNode(ctx, p); err == nil {
err := dht.peerstore.AddPubKey(p, pk)
if err != nil {
return pk, err
}
return pk, nil
}
// last ditch effort: let's try the dht.
log.Debugf("pk for %s not in peerstore, and peer failed. trying dht.", p)
pkkey := routing.KeyForPublicKey(p)
val, err := dht.GetValue(ctxT, pkkey)
if err != nil {
log.Warning("Failed to find requested public key.")
return nil, err
}
pk, err = ci.UnmarshalPublicKey(val)
if err != nil {
log.Debugf("Failed to unmarshal public key: %s", err)
return nil, err
}
return pk, dht.peerstore.AddPubKey(p, pk)
}
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 := routing.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 {
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
}
}
return dht.Validator.VerifyRecord(r)
}
// 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 {
if len(r.Signature) > 0 {
// get the public key, search for it if necessary.
p := peer.ID(r.GetAuthor())
pk, err := dht.GetPublicKey(ctx, p)
if err != nil {
return err
}
err = record.CheckRecordSig(r, pk)
if err != nil {
return err
}
}
return dht.Validator.VerifyRecord(r)
}