Skip to content

Commit

Permalink
Stop searching for public keys before doing an IPNS Get (#7549)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
aschmahmann committed Aug 14, 2020
1 parent dc869c9 commit 8e73021
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 54 deletions.
90 changes: 50 additions & 40 deletions namesys/ipns_resolver_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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())
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
Expand All @@ -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 {
Expand Down
14 changes: 0 additions & 14 deletions namesys/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8e73021

Please sign in to comment.