@cryptotaxi247 / kubo / commits / dbedee594

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