routing: record validation into record/
This commit moves the record validation/verification from dht/ into the new record/ packaage. Validator object -- which is merely a map of ValidatorFuncs -- with a VerifyRecord cc @whyrusleeping
Juan Batiz-Benet committed
Jan 17, 2015 at 03:52 UTC
56ae2fd0a82f7bfc424f3987b44ea478292b87e6
6 files changed
+89
-74
core/core.go
+1
-1
@@ -436,6 +436,6 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p
436
437
func constructDHTRouting(ctx context.Context, host p2phost.Host, ds datastore.ThreadSafeDatastore) (*dht.IpfsDHT, error) {
438
dhtRouting := dht.NewDHT(ctx, host, ds)
439
- dhtRouting.Validators[IpnsValidatorTag] = namesys.ValidateIpnsRecord
439
+ dhtRouting.Validator[IpnsValidatorTag] = namesys.ValidateIpnsRecord
440
return dhtRouting, nil
441
}
routing/dht/dht.go
+3
-4
@@ -54,8 +54,7 @@ type IpfsDHT struct {
54
birth time.Time // When this peer started up
55
diaglock sync.Mutex // lock to make diagnostics work better
56
57
- // record validator funcs
58
- Validators map[string]ValidatorFunc
57
+ Validator record.Validator // record validator funcs
58
59
ctxgroup.ContextGroup
60
}
@@ -81,8 +80,8 @@ func NewDHT(ctx context.Context, h host.Host, dstore ds.ThreadSafeDatastore) *Ip
80
dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(dht.self), time.Minute, dht.peerstore)
81
dht.birth = time.Now()
82
84
- dht.Validators = make(map[string]ValidatorFunc)
85
- dht.Validators["pk"] = ValidatePublicKeyRecord
83
+ dht.Validator = make(record.Validator)
84
+ dht.Validator["pk"] = record.ValidatePublicKeyRecord
85
86
if doPinging {
87
dht.Children().Add(1)
routing/dht/dht_test.go
+3
-3
@@ -38,7 +38,7 @@ func setupDHT(ctx context.Context, t *testing.T) *IpfsDHT {
38
dss := dssync.MutexWrap(ds.NewMapDatastore())
39
d := NewDHT(ctx, h, dss)
40
41
- d.Validators["v"] = func(u.Key, []byte) error {
41
+ d.Validator["v"] = func(u.Key, []byte) error {
42
return nil
43
}
44
return d
@@ -142,8 +142,8 @@ func TestValueGetSet(t *testing.T) {
142
vf := func(u.Key, []byte) error {
143
return nil
144
}
145
- dhtA.Validators["v"] = vf
146
- dhtB.Validators["v"] = vf
145
+ dhtA.Validator["v"] = vf
146
+ dhtB.Validator["v"] = vf
147
148
connect(t, ctx, dhtA, dhtB)
149
routing/dht/records.go
+3
-66
@@ -1,33 +1,17 @@
1
package dht
2
3
import (
4
- "bytes"
5
- "errors"
4
"fmt"
7
- "strings"
5
6
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
7
8
ci "github.com/jbenet/go-ipfs/p2p/crypto"
12
- "github.com/jbenet/go-ipfs/p2p/peer"
9
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
10
pb "github.com/jbenet/go-ipfs/routing/dht/pb"
14
- record "github.com/jbenet/go-ipfs/routing/record"
11
u "github.com/jbenet/go-ipfs/util"
12
ctxutil "github.com/jbenet/go-ipfs/util/ctx"
13
)
14
19
-// ValidatorFunc is a function that is called to validate a given
20
-// type of DHTRecord.
21
-type ValidatorFunc func(u.Key, []byte) error
22
-
23
-// ErrBadRecord is returned any time a dht record is found to be
24
-// incorrectly formatted or signed.
25
-var ErrBadRecord = errors.New("bad dht record")
26
-
27
-// ErrInvalidRecordType is returned if a DHTRecord keys prefix
28
-// is not found in the Validator map of the DHT.
29
-var ErrInvalidRecordType = errors.New("invalid record keytype")
30
-
15
// KeyForPublicKey returns the key used to retrieve public keys
16
// from the dht.
17
func KeyForPublicKey(id peer.ID) u.Key {
@@ -123,7 +107,7 @@ func (dht *IpfsDHT) verifyRecordLocally(r *pb.Record) error {
107
return fmt.Errorf("do not have public key for %s", p)
108
}
109
126
- return dht.verifyRecord(r, pk)
110
+ return dht.Validator.VerifyRecord(r, pk)
111
}
112
113
// verifyRecordOnline verifies a record, searching the DHT for the public key
@@ -140,52 +124,5 @@ func (dht *IpfsDHT) verifyRecordOnline(ctx context.Context, r *pb.Record) error
124
return err
125
}
126
143
- return dht.verifyRecord(r, pk)
144
-}
145
-
146
-// TODO: make this an independent exported function.
147
-// it might be useful for users to have access to.
148
-func (dht *IpfsDHT) verifyRecord(r *pb.Record, pk ci.PubKey) error {
149
- // First, validate the signature
150
- blob := record.RecordBlobForSig(r)
151
- ok, err := pk.Verify(blob, r.GetSignature())
152
- if err != nil {
153
- log.Error("Signature verify failed.")
154
- return err
155
- }
156
- if !ok {
157
- log.Error("dht found a forged record! (ignored)")
158
- return ErrBadRecord
159
- }
160
-
161
- // Now, check validity func
162
- parts := strings.Split(r.GetKey(), "/")
163
- if len(parts) < 3 {
164
- log.Infof("Record key does not have validator: %s", u.Key(r.GetKey()))
165
- return nil
166
- }
167
-
168
- fnc, ok := dht.Validators[parts[1]]
169
- if !ok {
170
- log.Errorf("Unrecognized key prefix: %s", parts[1])
171
- return ErrInvalidRecordType
172
- }
173
-
174
- return fnc(u.Key(r.GetKey()), r.GetValue())
175
-}
176
-
177
-// ValidatePublicKeyRecord implements ValidatorFunc and
178
-// verifies that the passed in record value is the PublicKey
179
-// that matches the passed in key.
180
-func ValidatePublicKeyRecord(k u.Key, val []byte) error {
181
- keyparts := bytes.Split([]byte(k), []byte("/"))
182
- if len(keyparts) < 3 {
183
- return errors.New("invalid key")
184
- }
185
-
186
- pkh := u.Hash(val)
187
- if !bytes.Equal(keyparts[2], pkh) {
188
- return errors.New("public key does not match storage key")
189
- }
190
- return nil
127
+ return dht.Validator.VerifyRecord(r, pk)
128
}
routing/record/record.go
+4
@@ -2,13 +2,17 @@ package record
2
3
import (
4
"bytes"
5
+
6
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
7
8
ci "github.com/jbenet/go-ipfs/p2p/crypto"
9
pb "github.com/jbenet/go-ipfs/routing/dht/pb"
10
+ eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"
11
u "github.com/jbenet/go-ipfs/util"
12
)
13
14
+var log = eventlog.Logger("routing/record")
15
+
16
// MakePutRecord creates and signs a dht record for the given key/value pair
17
func MakePutRecord(sk ci.PrivKey, key u.Key, value []byte) (*pb.Record, error) {
18
record := new(pb.Record)
routing/record/validation.go
new
+75
@@ -0,0 +1,75 @@
1
+package record
2
+
3
+import (
4
+ "bytes"
5
+ "errors"
6
+ "strings"
7
+
8
+ ci "github.com/jbenet/go-ipfs/p2p/crypto"
9
+ pb "github.com/jbenet/go-ipfs/routing/dht/pb"
10
+ u "github.com/jbenet/go-ipfs/util"
11
+)
12
+
13
+// ValidatorFunc is a function that is called to validate a given
14
+// type of DHTRecord.
15
+type ValidatorFunc func(u.Key, []byte) error
16
+
17
+// ErrBadRecord is returned any time a dht record is found to be
18
+// incorrectly formatted or signed.
19
+var ErrBadRecord = errors.New("bad dht record")
20
+
21
+// ErrInvalidRecordType is returned if a DHTRecord keys prefix
22
+// is not found in the Validator map of the DHT.
23
+var ErrInvalidRecordType = errors.New("invalid record keytype")
24
+
25
+// Validator is an object that helps ensure routing records are valid.
26
+// It is a collection of validator functions, each of which implements
27
+// its own notion of validity.
28
+type Validator map[string]ValidatorFunc
29
+
30
+// VerifyRecord checks a record and ensures it is still valid.
31
+// It runs needed validators
32
+func (v Validator) VerifyRecord(r *pb.Record, pk ci.PubKey) error {
33
+ // First, validate the signature
34
+ blob := RecordBlobForSig(r)
35
+ ok, err := pk.Verify(blob, r.GetSignature())
36
+ if err != nil {
37
+ log.Error("Signature verify failed.")
38
+ return err
39
+ }
40
+ if !ok {
41
+ log.Error("dht found a forged record! (ignored)")
42
+ return ErrBadRecord
43
+ }
44
+
45
+ // Now, check validity func
46
+ parts := strings.Split(r.GetKey(), "/")
47
+ if len(parts) < 3 {
48
+ log.Infof("Record key does not have validator: %s", u.Key(r.GetKey()))
49
+ return nil
50
+ }
51
+
52
+ fnc, ok := v[parts[1]]
53
+ if !ok {
54
+ log.Errorf("Unrecognized key prefix: %s", parts[1])
55
+ return ErrInvalidRecordType
56
+ }
57
+
58
+ return fnc(u.Key(r.GetKey()), r.GetValue())
59
+}
60
+
61
+// ValidatePublicKeyRecord implements ValidatorFunc and
62
+// verifies that the passed in record value is the PublicKey
63
+// that matches the passed in key.
64
+func ValidatePublicKeyRecord(k u.Key, val []byte) error {
65
+ keyparts := bytes.Split([]byte(k), []byte("/"))
66
+ if len(keyparts) < 3 {
67
+ return errors.New("invalid key")
68
+ }
69
+
70
+ pkh := u.Hash(val)
71
+ if !bytes.Equal(keyparts[2], pkh) {
72
+ return errors.New("public key does not match storage key")
73
+ }
74
+ return nil
75
+}