Update ipns validator
License: MIT Signed-off-by: Dirk McCormick <dirkmdev@gmail.com>
Dirk McCormick committed
Jan 20, 2018 at 17:25 UTC
281d5eafb4ed763047c70b8097881ab2a5654e4b
2 files changed
+123
-2
namesys/ipns_validate_test.go
new
+93
@@ -0,0 +1,93 @@
1
+package namesys
2
+
3
+import (
4
+ "testing"
5
+ "time"
6
+
7
+ path "github.com/ipfs/go-ipfs/path"
8
+ u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
9
+ proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
10
+ ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto"
11
+ record "gx/ipfs/QmbsY8Pr6s3uZsKg7rzBtGDKeCtdoAhNaMTCXBUbvb1eCV/go-libp2p-record"
12
+)
13
+
14
+func TestValidation(t *testing.T) {
15
+ // Create a record validator
16
+ validator := make(record.Validator)
17
+ validator["ipns"] = &record.ValidChecker{ValidateIpnsRecord, true}
18
+
19
+ // Generate a key for signing the records
20
+ r := u.NewSeededRand(15) // generate deterministic keypair
21
+ priv, pubk, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r)
22
+ if err != nil {
23
+ t.Fatal(err)
24
+ }
25
+
26
+ // Create entry with expiry in one hour
27
+ ts := time.Now()
28
+ entry, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(time.Hour))
29
+ if err != nil {
30
+ t.Fatal(err)
31
+ }
32
+
33
+ // Get IPNS record path
34
+ pubkb, err := pubk.Bytes()
35
+ if err != nil {
36
+ t.Fatal(err)
37
+ }
38
+ pubkh := u.Hash(pubkb).B58String()
39
+ ipnsPath := "/ipns/" + pubkh
40
+
41
+ val, err := proto.Marshal(entry)
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+
46
+ // Create the record
47
+ r1, err := record.MakePutRecord(priv, ipnsPath, val, true)
48
+
49
+ // Validate the record
50
+ err = validator.VerifyRecord(r1)
51
+ if err != nil {
52
+ t.Fatal(err)
53
+ }
54
+
55
+ // Create IPNS record path with a different key
56
+ _, pubk2, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r)
57
+ if err != nil {
58
+ t.Fatal(err)
59
+ }
60
+ pubkb2, err := pubk2.Bytes()
61
+ if err != nil {
62
+ t.Fatal(err)
63
+ }
64
+ pubkh2 := u.Hash(pubkb2).B58String()
65
+ ipnsWrongPath := "/ipns/" + pubkh2
66
+
67
+ r2, err := record.MakePutRecord(priv, ipnsWrongPath, val, true)
68
+
69
+ // Record should fail validation because path doesn't match author
70
+ err = validator.VerifyRecord(r2)
71
+ if err != ErrInvalidAuthor {
72
+ t.Fatal("ValidateIpnsRecord should have returned ErrInvalidAuthor")
73
+ }
74
+
75
+ // Create expired entry
76
+ expired, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(-1*time.Hour))
77
+ if err != nil {
78
+ t.Fatal(err)
79
+ }
80
+ valExp, err := proto.Marshal(expired)
81
+ if err != nil {
82
+ t.Fatal(err)
83
+ }
84
+
85
+ // Create record with the expired entry
86
+ r3, err := record.MakePutRecord(priv, ipnsPath, valExp, true)
87
+
88
+ // Record should fail validation because entry is expired
89
+ err = validator.VerifyRecord(r3)
90
+ if err != ErrExpiredRecord {
91
+ t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord")
92
+ }
93
+}
namesys/publisher.go
+30
-2
@@ -5,6 +5,7 @@ import (
5
"context"
6
"errors"
7
"fmt"
8
+ "strings"
9
"time"
10
11
pb "github.com/ipfs/go-ipfs/namesys/pb"
@@ -31,6 +32,14 @@ var ErrExpiredRecord = errors.New("expired record")
32
// unknown validity type.
33
var ErrUnrecognizedValidity = errors.New("unrecognized validity type")
34
35
+// ErrInvalidAuthor is returned when an IpnsRecord has an
36
+// author that does not match the IPNS path
37
+var ErrInvalidAuthor = errors.New("author does not match path")
38
+
39
+// ErrInvalidPath should be returned when an ipns record path
40
+// is not in a valid format
41
+var ErrInvalidPath = errors.New("record path invalid")
42
+
43
const PublishPutValTimeout = time.Minute
44
const DefaultRecordTTL = 24 * time.Hour
45
@@ -295,12 +304,31 @@ func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {
304
305
// ValidateIpnsRecord implements ValidatorFunc and verifies that the
306
// given 'val' is an IpnsEntry and that that entry is valid.
298
-func ValidateIpnsRecord(k string, val []byte) error {
307
+func ValidateIpnsRecord(r *record.ValidationRecord) error {
308
+ if r.Namespace != "ipns" {
309
+ return ErrInvalidPath
310
+ }
311
+
312
entry := new(pb.IpnsEntry)
300
- err := proto.Unmarshal(val, entry)
313
+ err := proto.Unmarshal(r.Value, entry)
314
if err != nil {
315
return err
316
}
317
+
318
+ // Note: The DHT will actually check the signature so we don't
319
+ // need to do that here
320
+
321
+ // Author in key must match author in record
322
+ parts := strings.Split(r.Key, "/")
323
+ pid, err := peer.IDB58Decode(parts[0])
324
+ if err != nil {
325
+ return ErrInvalidAuthor
326
+ }
327
+ if string(pid) != string(r.Author) {
328
+ return ErrInvalidAuthor
329
+ }
330
+
331
+ // Check that record has not expired
332
switch entry.GetValidityType() {
333
case pb.IpnsEntry_EOL:
334
t, err := u.ParseRFC3339(string(entry.GetValidity()))