From 47b5f26d46cac376261f518477f3719233937181 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Mon, 6 Jul 2020 13:08:08 -0400 Subject: [PATCH 1/2] feat: stop checking the DHT for public keys before doing an IPNS get. It has been many releases since we started adding the public keys into the IPNS records by default. --- namesys/routing.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/namesys/routing.go b/namesys/routing.go index 60928fbca69..8bdfe21e65b 100644 --- a/namesys/routing.go +++ b/namesys/routing.go @@ -69,20 +69,6 @@ func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, option return out } - // Name should be the hash of a public key retrievable from ipfs. - // We retrieve the public key here to make certain that it's in the peer - // store before calling GetValue() on the DHT - the DHT will call the - // ipns validator, which in turn will get the public key from the peer - // store to verify the record signature - _, err = routing.GetPublicKey(r.routing, ctx, pid) - if err != nil { - log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err) - out <- onceResult{err: err} - close(out) - cancel() - return out - } - // Use the routing system to get the name. // Note that the DHT will call the ipns validator when retrieving // the value, which in turn verifies the ipns record signature From 55d5badaa536a76db2575ff0d1913574b3dde9ac Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Fri, 17 Jul 2020 17:05:57 -0400 Subject: [PATCH 2/2] fix tests --- namesys/ipns_resolver_validation_test.go | 90 +++++++++++++----------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/namesys/ipns_resolver_validation_test.go b/namesys/ipns_resolver_validation_test.go index 1fd7488b9ed..1e342f25904 100644 --- a/namesys/ipns_resolver_validation_test.go +++ b/namesys/ipns_resolver_validation_test.go @@ -10,6 +10,7 @@ import ( mockrouting "github.com/ipfs/go-ipfs-routing/mock" offline "github.com/ipfs/go-ipfs-routing/offline" ipns "github.com/ipfs/go-ipns" + ipns_pb "github.com/ipfs/go-ipns/pb" path "github.com/ipfs/go-path" opts "github.com/ipfs/interface-go-ipfs-core/options/namesys" ci "github.com/libp2p/go-libp2p-core/crypto" @@ -23,6 +24,25 @@ import ( ) func TestResolverValidation(t *testing.T) { + t.Run("RSA", + func(t *testing.T) { + testResolverValidation(t, ci.RSA) + }) + t.Run("Ed25519", + func(t *testing.T) { + testResolverValidation(t, ci.Ed25519) + }) + t.Run("ECDSA", + func(t *testing.T) { + testResolverValidation(t, ci.ECDSA) + }) + t.Run("Secp256k1", + func(t *testing.T) { + testResolverValidation(t, ci.Secp256k1) + }) +} + +func testResolverValidation(t *testing.T, keyType int) { ctx := context.Background() rid := testutil.RandIdentityOrFatal(t) dstore := dssync.MutexWrap(ds.NewMapDatastore()) @@ -34,16 +54,10 @@ func TestResolverValidation(t *testing.T) { nvVstore := offline.NewOfflineRouter(dstore, mockrouting.MockValidator{}) // Create entry with expiry in one hour - priv, id, _, ipnsDHTPath := genKeys(t) + priv, id, _, ipnsDHTPath := genKeys(t, keyType) ts := time.Now() p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG") - entry, err := ipns.Create(priv, p, 1, ts.Add(time.Hour)) - if err != nil { - t.Fatal(err) - } - - // Make peer's public key available in peer store - err = peerstore.AddPubKey(id, priv.GetPublic()) + entry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(time.Hour)) if err != nil { t.Fatal(err) } @@ -63,7 +77,7 @@ func TestResolverValidation(t *testing.T) { t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp) } // Create expired entry - expiredEntry, err := ipns.Create(priv, p, 1, ts.Add(-1*time.Hour)) + expiredEntry, err := createIPNSRecordWithEmbeddedPublicKey(priv, p, 1, ts.Add(-1*time.Hour)) if err != nil { t.Fatal(err) } @@ -81,13 +95,7 @@ func TestResolverValidation(t *testing.T) { } // Create IPNS record path with a different private key - priv2, id2, _, ipnsDHTPath2 := genKeys(t) - - // Make peer's public key available in peer store - err = peerstore.AddPubKey(id2, priv2.GetPublic()) - if err != nil { - t.Fatal(err) - } + priv2, id2, _, ipnsDHTPath2 := genKeys(t, keyType) // Publish entry err = PublishEntry(ctx, nvVstore, ipnsDHTPath2, entry) @@ -102,50 +110,52 @@ func TestResolverValidation(t *testing.T) { t.Fatal("ValidateIpnsRecord should have failed signature verification") } - // Publish entry without making public key available in peer store - priv3, id3, pubkDHTPath3, ipnsDHTPath3 := genKeys(t) - entry3, err := ipns.Create(priv3, p, 1, ts.Add(time.Hour)) - if err != nil { + // Try embedding the incorrect private key inside the entry + if err := ipns.EmbedPublicKey(priv2.GetPublic(), entry); err != nil { t.Fatal(err) } - err = PublishEntry(ctx, nvVstore, ipnsDHTPath3, entry3) + + // Publish entry + err = PublishEntry(ctx, nvVstore, ipnsDHTPath2, entry) if err != nil { t.Fatal(err) } - // Record should fail validation because public key is not available - // in peer store or on network - _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts()) + // Record should fail validation because public key defined by + // ipns path doesn't match record signature + _, err = resolve(ctx, resolver, id2.Pretty(), opts.DefaultResolveOpts()) if err == nil { - t.Fatal("ValidateIpnsRecord should have failed because public key was not found") + t.Fatal("ValidateIpnsRecord should have failed signature verification") + } +} + +func genKeys(t *testing.T, keyType int) (ci.PrivKey, peer.ID, string, string) { + bits := 0 + if keyType == ci.RSA { + bits = 2048 } - // Publish public key to the network - err = PublishPublicKey(ctx, vstore, pubkDHTPath3, priv3.GetPublic()) + sk, pk, err := test.RandTestKeyPair(keyType, bits) if err != nil { t.Fatal(err) } - - // Record should now pass validation because resolver will ensure - // public key is available in the peer store by looking it up in - // the DHT, which causes the DHT to fetch it and cache it in the - // peer store - _, err = resolve(ctx, resolver, id3.Pretty(), opts.DefaultResolveOpts()) + id, err := peer.IDFromPublicKey(pk) if err != nil { t.Fatal(err) } + return sk, id, PkKeyForID(id), ipns.RecordKey(id) } -func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) { - sk, pk, err := test.RandTestKeyPair(ci.RSA, 2048) +func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error){ + entry, err := ipns.Create(sk, val, seq, eol) if err != nil { - t.Fatal(err) + return nil, err } - id, err := peer.IDFromPublicKey(pk) - if err != nil { - t.Fatal(err) + if err := ipns.EmbedPublicKey(sk.GetPublic(), entry); err != nil { + return nil, err } - return sk, id, PkKeyForID(id), ipns.RecordKey(id) + + return entry, nil } type mockValueStore struct {