Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop searching for public keys before doing an IPNS Get #7549

Merged
merged 2 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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