namesys: differentiate between validation errors License: MIT Signed-off-by: Dirk McCormick <dirkmdev@gmail.com>
namesys: differentiate between validation errors License: MIT Signed-off-by: Dirk McCormick <dirkmdev@gmail.com>
Dirk McCormick committed
Jan 31, 2018 at 00:37 UTC
dbedee594082ee68726e7a846ac93a9cfe5c32d5
4 files changed
+78
-6
namesys/ipns_validate_test.go
+57
-1
@@ -2,6 +2,7 @@ package namesys
2
3
import (
4
"context"
5
+ "fmt"
6
"testing"
7
"time"
8
@@ -21,7 +22,62 @@ import (
22
ci "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto"
23
)
24
24
-func TestValidation(t *testing.T) {
25
+func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, ns string, key string, val []byte, eol time.Time, exp error) {
26
+ validChecker := NewIpnsRecordValidator(kbook)
27
+
28
+ p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG")
29
+ entry, err := CreateRoutingEntryData(priv, p, 1, eol)
30
+ if err != nil {
31
+ t.Fatal(err)
32
+ }
33
+
34
+ data := val
35
+ if data == nil {
36
+ data, err = proto.Marshal(entry)
37
+ if err != nil {
38
+ t.Fatal(err)
39
+ }
40
+ }
41
+ rec := &record.ValidationRecord{
42
+ Namespace: ns,
43
+ Key: key,
44
+ Value: data,
45
+ }
46
+
47
+ err = validChecker.Func(rec)
48
+ if err != exp {
49
+ params := fmt.Sprintf("namespace: %s\nkey: %s\neol: %s\n", ns, key, eol)
50
+ if exp == nil {
51
+ t.Fatalf("Unexpected error %s for params %s", err, params)
52
+ } else if err == nil {
53
+ t.Fatalf("Expected error %s but there was no error for params %s", exp, params)
54
+ } else {
55
+ t.Fatalf("Expected error %s but got %s for params %s", exp, err, params)
56
+ }
57
+ }
58
+}
59
+
60
+func TestValidator(t *testing.T) {
61
+ ts := time.Now()
62
+
63
+ priv, id, _, _ := genKeys(t)
64
+ priv2, id2, _, _ := genKeys(t)
65
+ kbook := pstore.NewPeerstore()
66
+ kbook.AddPubKey(id, priv.GetPublic())
67
+ emptyKbook := pstore.NewPeerstore()
68
+
69
+ testValidatorCase(t, priv, kbook, "ipns", string(id), nil, ts.Add(time.Hour), nil)
70
+ testValidatorCase(t, priv, kbook, "ipns", string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord)
71
+ testValidatorCase(t, priv, kbook, "ipns", string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord)
72
+ testValidatorCase(t, priv, kbook, "ipns", "bad key", nil, ts.Add(time.Hour), ErrKeyFormat)
73
+ testValidatorCase(t, priv, emptyKbook, "ipns", string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound)
74
+ testValidatorCase(t, priv2, kbook, "ipns", string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound)
75
+ testValidatorCase(t, priv2, kbook, "ipns", string(id), nil, ts.Add(time.Hour), ErrSignature)
76
+ testValidatorCase(t, priv, kbook, "", string(id), nil, ts.Add(time.Hour), ErrInvalidPath)
77
+ testValidatorCase(t, priv, kbook, "wrong", string(id), nil, ts.Add(time.Hour), ErrInvalidPath)
78
+}
79
+
80
+func TestResolverValidation(t *testing.T) {
81
ctx := context.Background()
82
rid := testutil.RandIdentityOrFatal(t)
83
dstore := dssync.MutexWrap(ds.NewMapDatastore())
namesys/routing.go
+3
-1
@@ -147,7 +147,9 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa
147
return "", err
148
}
149
150
- // use the routing system to get the name.
150
+ // Use the routing system to get the name.
151
+ // Note that the DHT will call the ipns validator when retrieving
152
+ // the value, which in turn verifies the ipns record signature
153
_, ipnsKey := IpnsKeysForID(pid)
154
val, err := r.routing.GetValue(ctx, ipnsKey)
155
if err != nil {
namesys/selector.go
+2
@@ -42,11 +42,13 @@ func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {
42
} else if r.GetSequence() == bestSeq {
43
rt, err := u.ParseRFC3339(string(r.GetValidity()))
44
if err != nil {
45
+ log.Errorf("failed to parse ipns record EOL %s", r.GetValidity())
46
continue
47
}
48
49
bestt, err := u.ParseRFC3339(string(recs[besti].GetValidity()))
50
if err != nil {
51
+ log.Errorf("failed to parse ipns record EOL %s", recs[besti].GetValidity())
52
continue
53
}
54
namesys/validator.go
+16
-4
@@ -29,6 +29,18 @@ var ErrInvalidPath = errors.New("record path invalid")
29
// signature verification
30
var ErrSignature = errors.New("record signature verification failed")
31
32
+// ErrBadRecord should be returned when an ipns record cannot be unmarshalled
33
+var ErrBadRecord = errors.New("record could not be unmarshalled")
34
+
35
+// ErrKeyFormat should be returned when an ipns record key is
36
+// incorrectly formatted (not a peer ID)
37
+var ErrKeyFormat = errors.New("record key could not be parsed into peer ID")
38
+
39
+// ErrPublicKeyNotFound should be returned when the public key
40
+// corresponding to the ipns record path cannot be retrieved
41
+// from the peer store
42
+var ErrPublicKeyNotFound = errors.New("public key not found in peer store")
43
+
44
// NewIpnsRecordValidator returns a ValidChecker for IPNS records
45
// The validator function will get a public key from the KeyBook
46
// to verify the record's signature
@@ -44,19 +56,19 @@ func NewIpnsRecordValidator(kbook pstore.KeyBook) *record.ValidChecker {
56
entry := new(pb.IpnsEntry)
57
err := proto.Unmarshal(r.Value, entry)
58
if err != nil {
47
- return err
59
+ return ErrBadRecord
60
}
61
62
// Get the public key defined by the ipns path
63
pid, err := peer.IDFromString(r.Key)
64
if err != nil {
53
- log.Debugf("failed to parse ipns record key %s into public key hash", r.Key)
54
- return ErrSignature
65
+ log.Debugf("failed to parse ipns record key %s into peer ID", r.Key)
66
+ return ErrKeyFormat
67
}
68
pubk := kbook.PubKey(pid)
69
if pubk == nil {
70
log.Debugf("public key with hash %s not found in peer store", pid)
59
- return ErrSignature
71
+ return ErrPublicKeyNotFound
72
}
73
74
// Check the ipns record signature with the public key