veyron2/ipc,veyron/runtimes/google/ipc: Support for the newer security model.

This commit makes the IPC model (and the corresponding implementation in
veyron/runtimes/google/ipc) respect the new security model (of Principal and
Blessings). All tests are updated to use only the new model (i.e., not use
PrivateID and PublicID).

This commit does break some features, but the author's assessment is that these
features are not widely used enough yet to warrant concern about backward
compatibility. For example: IPCs where clients currently grant blessings to
servers will not work (since the ipc.Request message has changed), and
ThirdPartyCaveat usage with the old model will not function (discharges will
not be fetched by the IPC stack).

(The only known client of the granted blessings was the nodemanager Claim
method, tests for which have been temporarily disabled)

Future steps (towards removing the old model completely):
(1) Update the veyron/runtimes/google/rt implementation so that it
    sets up IPC clients and servers with the new primitives
(2) Update the identityd server and identity tool to use the new
    primitives
(3) Replace application code using the old primitives with new.

Change-Id: Ie9a9d3b3dba5ef8d9a6fc22b5f6bc09ee3f02cbe
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index f712c0a..0e9c171 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -41,8 +41,8 @@
 	// vcMap.  Everything else is initialized upon client construction, and safe
 	// to use concurrently.
 	vcMapMu sync.Mutex
-	// TODO(ashankar): Additionally, should vcMap be keyed with other options also?
-	vcMap map[string]*vcInfo // map from endpoint.String() to vc info
+	// TODO(ashankar): The key should be a function of the blessings shared with the server?
+	vcMap map[string]*vcInfo // map key is endpoint.String
 
 	dischargeCache dischargeCache
 }
@@ -50,7 +50,6 @@
 type vcInfo struct {
 	vc       stream.VC
 	remoteEP naming.Endpoint
-	// TODO(toddw): Add type and cancel flows.
 }
 
 func InternalNewClient(streamMgr stream.Manager, ns naming.Namespace, opts ...ipc.ClientOpt) (ipc.Client, error) {
@@ -88,7 +87,6 @@
 	if err != nil {
 		return nil, err
 	}
-	// TODO(toddw): Add connections for the type and cancel flows.
 	c.vcMap[ep.String()] = &vcInfo{vc: vc, remoteEP: ep}
 	return vc.Connect()
 }
@@ -237,17 +235,22 @@
 		flow.SetDeadline(ctx.Done())
 
 		// Validate caveats on the server's identity for the context associated with this call.
-		blessing, err := authorizeServer(flow.LocalID(), flow.RemoteID(), opts)
+		serverB, grantedB, err := c.authorizeServer(flow, name, suffix, method, opts)
 		if err != nil {
-			lastErr = verror.NoAccessf("ipc: client unwilling to talk to server %q: %v", flow.RemoteID(), err)
+			lastErr = verror.NoAccessf("ipc: client unwilling to invoke %q.%q on server %v: %v", name, method, flow.RemoteBlessings(), err)
 			flow.Close()
 			continue
 		}
-
-		discharges := c.prepareDischarges(ctx, flow.LocalID(), flow.RemoteID(), method, args, opts)
+		// Fetch any discharges for third-party caveats on the client's blessings.
+		var discharges []security.Discharge
+		if self := flow.LocalBlessings(); self != nil {
+			if tpcavs := self.ThirdPartyCaveats(); len(tpcavs) > 0 {
+				discharges = c.prepareDischarges(ctx, tpcavs, mkDischargeImpetus(serverB, method, args), opts)
+			}
+		}
 
 		lastErr = nil
-		fc := newFlowClient(ctx, flow, &c.dischargeCache, discharges)
+		fc := newFlowClient(ctx, serverB, flow, &c.dischargeCache, discharges)
 
 		if doneChan := ctx.Done(); doneChan != nil {
 			go func() {
@@ -263,7 +266,7 @@
 		if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
 			timeout = deadline.Sub(time.Now())
 		}
-		if verr := fc.start(suffix, method, args, timeout, blessing); verr != nil {
+		if verr := fc.start(suffix, method, args, timeout, grantedB); verr != nil {
 			return nil, verr
 		}
 		return fc, nil
@@ -277,43 +280,55 @@
 	return nil, errNoServers
 }
 
-// authorizeServer validates that server has an identity that the client is willing to converse
-// with, and if so returns a blessing to be provided to the server. This blessing can be nil,
-// which indicates that the client does wish to talk to the server but not provide any blessings.
-func authorizeServer(client, server security.PublicID, opts []ipc.CallOpt) (security.PublicID, error) {
-	if server == nil {
-		return nil, fmt.Errorf("server identity cannot be nil")
+// authorizeServer validates that the server (remote end of flow) has the credentials to serve
+// the RPC name.method for the client (local end of the flow). It returns the blessings at the
+// server that are authorized for this purpose and any blessings that are to be granted to
+// the server (via ipc.Granter implementations in opts.)
+func (c *client) authorizeServer(flow stream.Flow, name, suffix, method string, opts []ipc.CallOpt) (serverBlessings []string, grantedBlessings security.Blessings, err error) {
+	if flow.RemoteID() == nil && flow.RemoteBlessings() == nil {
+		return nil, nil, fmt.Errorf("server has not presented any blessings")
 	}
-
-	// TODO(ataly): What should the label be for the context? Typically the label is the
-	// security.Label of the method but we don't have that information here at the client.
-	authID, err := server.Authorize(isecurity.NewContext(isecurity.ContextArgs{
-		LocalID:  client,
-		RemoteID: server,
-	}))
-	if err != nil {
-		return nil, err
+	authctx := isecurity.NewContext(isecurity.ContextArgs{
+		LocalID:         flow.LocalID(),
+		RemoteID:        flow.RemoteID(),
+		Debug:           "ClientAuthorizingServer",
+		LocalPrincipal:  flow.LocalPrincipal(),
+		LocalBlessings:  flow.LocalBlessings(),
+		RemoteBlessings: flow.RemoteBlessings(),
+		/* TODO(ashankar,ataly): Uncomment this! This is disabled till the hack to skip third-party caveat
+		validation on a server's blessings are disabled. Commenting out the next three lines affects more
+		than third-party caveats, so yeah, have to remove this soon!
+		Method:          method,
+		Name:            name,
+		Suffix:          suffix, */
+		LocalEndpoint:  flow.LocalEndpoint(),
+		RemoteEndpoint: flow.RemoteEndpoint(),
+	})
+	if serverID := flow.RemoteID(); flow.RemoteBlessings() == nil && serverID != nil {
+		serverID, err = serverID.Authorize(authctx)
+		if err != nil {
+			return nil, nil, err
+		}
+		serverBlessings = serverID.Names()
 	}
-	var granter ipc.Granter
+	if server := flow.RemoteBlessings(); server != nil {
+		serverBlessings = server.ForContext(authctx)
+	}
 	for _, o := range opts {
 		switch v := o.(type) {
 		case veyron2.RemoteID:
-			if !security.BlessingPattern(v).MatchedBy(authID.Names()...) {
-				return nil, fmt.Errorf("server %q does not match the provided pattern %q", authID, v)
+			if !security.BlessingPattern(v).MatchedBy(serverBlessings...) {
+				return nil, nil, fmt.Errorf("server %v does not match the provided pattern %q", serverBlessings, v)
 			}
 		case ipc.Granter:
-			// Later Granters take precedence over earlier ones.
-			// Or should fail if there are multiple provided?
-			granter = v
+			if b, err := v.Grant(flow.RemoteBlessings()); err != nil {
+				return nil, nil, fmt.Errorf("failed to grant blessing to server %v: %v", serverBlessings, err)
+			} else if grantedBlessings, err = security.UnionOfBlessings(grantedBlessings, b); err != nil {
+				return nil, nil, fmt.Errorf("failed to add blessing granted to server %v: %v", serverBlessings, err)
+			}
 		}
 	}
-	var blessing security.PublicID
-	if granter != nil {
-		if blessing, err = granter.Grant(authID); err != nil {
-			return nil, fmt.Errorf("failed to grant credentials to server %q: %v", authID, err)
-		}
-	}
-	return blessing, nil
+	return serverBlessings, grantedBlessings, nil
 }
 
 func (c *client) Close() {
@@ -339,6 +354,7 @@
 	ctx      context.T    // context to annotate with call details
 	dec      *vom.Decoder // to decode responses and results from the server
 	enc      *vom.Encoder // to encode requests and args to the server
+	server   []string     // Blessings bound to the server that authorize it to receive the IPC request from the client.
 	flow     stream.Flow  // the underlying flow
 	response ipc.Response // each decoded response message is kept here
 
@@ -351,12 +367,12 @@
 	finished bool // has Finish() already been called?
 }
 
-func newFlowClient(ctx context.T, flow stream.Flow, dischargeCache *dischargeCache, discharges []security.Discharge) *flowClient {
+func newFlowClient(ctx context.T, server []string, flow stream.Flow, dischargeCache *dischargeCache, discharges []security.Discharge) *flowClient {
 	return &flowClient{
-		// TODO(toddw): Support different codecs
 		ctx:            ctx,
 		dec:            vom.NewDecoder(flow),
 		enc:            vom.NewEncoder(flow),
+		server:         server,
 		flow:           flow,
 		discharges:     discharges,
 		dischargeCache: dischargeCache,
@@ -370,25 +386,19 @@
 	return verr
 }
 
-func (fc *flowClient) start(suffix, method string, args []interface{}, timeout time.Duration, blessing security.PublicID) verror.E {
-
+func (fc *flowClient) start(suffix, method string, args []interface{}, timeout time.Duration, blessings security.Blessings) verror.E {
 	req := ipc.Request{
-		Suffix:        suffix,
-		Method:        method,
-		NumPosArgs:    uint64(len(args)),
-		Timeout:       int64(timeout),
-		HasBlessing:   blessing != nil,
-		NumDischarges: uint64(len(fc.discharges)),
-		TraceRequest:  vtrace.Request(fc.ctx),
+		Suffix:           suffix,
+		Method:           method,
+		NumPosArgs:       uint64(len(args)),
+		Timeout:          int64(timeout),
+		GrantedBlessings: security.MarshalBlessings(blessings),
+		NumDischarges:    uint64(len(fc.discharges)),
+		TraceRequest:     vtrace.Request(fc.ctx),
 	}
 	if err := fc.enc.Encode(req); err != nil {
 		return fc.close(verror.BadProtocolf("ipc: request encoding failed: %v", err))
 	}
-	if blessing != nil {
-		if err := fc.enc.Encode(blessing); err != nil {
-			return fc.close(verror.BadProtocolf("ipc: blessing encoding failed: %v", err))
-		}
-	}
 	for _, d := range fc.discharges {
 		if err := fc.enc.Encode(d); err != nil {
 			return fc.close(verror.BadProtocolf("ipc: failed to encode discharge for %x: %v", d.ID(), err))
@@ -551,6 +561,5 @@
 }
 
 func (fc *flowClient) RemoteBlessings() ([]string, security.Blessings) {
-	// TODO(ashankar): Fill in the second result once the switch to the new API is complete.
-	return fc.flow.RemoteID().Names(), nil
+	return fc.server, fc.flow.RemoteBlessings()
 }
diff --git a/runtimes/google/ipc/debug_test.go b/runtimes/google/ipc/debug_test.go
index 887bdec..f18f175 100644
--- a/runtimes/google/ipc/debug_test.go
+++ b/runtimes/google/ipc/debug_test.go
@@ -13,14 +13,26 @@
 	"veyron.io/veyron/veyron/lib/stats"
 	"veyron.io/veyron/veyron/profiles"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
 	tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
 )
 
 func TestDebugServer(t *testing.T) {
+	// Setup the client and server principals, with the client willing to share its
+	// blessing with the server.
+	var (
+		pclient = sectest.NewPrincipal("client")
+		pserver = sectest.NewPrincipal("server")
+		bclient = bless(pserver, pclient, "client") // server/client blessing.
+	)
+	pclient.AddToRoots(bclient)                    // Client recognizes "server" as a root of blessings.
+	pclient.BlessingStore().Set(bclient, "server") // Client presents bclient to server
+
 	sm := manager.InternalNew(naming.FixedRoutingID(0x555555555))
 	defer sm.Shutdown()
 	ns := tnaming.NewSimpleNamespace()
-	server, err := InternalNewServer(testContext(), sm, ns)
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{pserver})
 	if err != nil {
 		t.Fatalf("InternalNewServer failed: %v", err)
 	}
@@ -30,7 +42,7 @@
 	if err != nil {
 		t.Fatalf("server.Listen failed: %v", err)
 	}
-	client, err := InternalNewClient(sm, ns)
+	client, err := InternalNewClient(sm, ns, vc.LocalPrincipal{pclient})
 	if err != nil {
 		t.Fatalf("InternalNewClient failed: %v", err)
 	}
diff --git a/runtimes/google/ipc/discharges.go b/runtimes/google/ipc/discharges.go
index 88fbfb0..d88c31c 100644
--- a/runtimes/google/ipc/discharges.go
+++ b/runtimes/google/ipc/discharges.go
@@ -11,31 +11,40 @@
 	"veyron.io/veyron/veyron2/vlog"
 )
 
-// prepareDischarges retrieves the caveat discharges required for using blessing
+func mkDischargeImpetus(serverBlessings []string, method string, args []interface{}) security.DischargeImpetus {
+	var impetus security.DischargeImpetus
+	if len(serverBlessings) > 0 {
+		impetus.Server = make([]security.BlessingPattern, len(serverBlessings))
+		for i, b := range serverBlessings {
+			impetus.Server[i] = security.BlessingPattern(b)
+		}
+	}
+	impetus.Method = method
+	if len(args) > 0 {
+		impetus.Arguments = make([]vdlutil.Any, len(args))
+		for i, a := range args {
+			impetus.Arguments[i] = vdlutil.Any(a)
+		}
+	}
+	return impetus
+}
+
+// prepareDischarges retrieves the caveat discharges required for using blessings
 // at server. The discharges are either found in the client cache, in the call
 // options, or requested from the discharge issuer indicated on the caveat.
 // Note that requesting a discharge is an ipc call, so one copy of this
 // function must be able to successfully terminate while another is blocked.
-func (c *client) prepareDischarges(ctx context.T, blessing, server security.PublicID, method string, args []interface{}, opts []ipc.CallOpt) (ret []security.Discharge) {
-	// TODO(andreser,ataly): figure out whether this should return an error and how that should be handled
-	// Missing discharges do not necessarily mean the blessing is invalid (e.g., SetID)
-	if blessing == nil {
-		return
-	}
-
+func (c *client) prepareDischarges(ctx context.T, forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus, opts []ipc.CallOpt) (ret []security.Discharge) {
+	// Make a copy since this copy will be mutated.
 	var caveats []security.ThirdPartyCaveat
-	for _, cav := range blessing.ThirdPartyCaveats() {
+	for _, cav := range forcaveats {
 		caveats = append(caveats, cav)
 	}
-	if len(caveats) == 0 {
-		return
-	}
-
 	discharges := make([]security.Discharge, len(caveats))
 	dischargesFromOpts(caveats, opts, discharges)
 	c.dischargeCache.Discharges(caveats, discharges)
 	if shouldFetchDischarges(opts) {
-		c.fetchDischarges(ctx, caveats, server, method, args, opts, discharges)
+		c.fetchDischarges(ctx, caveats, impetus, opts, discharges)
 	}
 	for _, d := range discharges {
 		if d != nil {
@@ -110,7 +119,7 @@
 // caveats, fetchDischarges keeps retrying until either all discharges can be
 // fetched or no new discharges are fetched.
 // REQUIRES: len(caveats) == len(out)
-func (c *client) fetchDischarges(ctx context.T, caveats []security.ThirdPartyCaveat, server security.PublicID, method string, args []interface{}, opts []ipc.CallOpt, out []security.Discharge) {
+func (c *client) fetchDischarges(ctx context.T, caveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus, opts []ipc.CallOpt, out []security.Discharge) {
 	opts = append([]ipc.CallOpt{dontFetchDischarges{}}, opts...)
 	var wg sync.WaitGroup
 	for {
@@ -127,7 +136,7 @@
 			go func(i int, cav security.ThirdPartyCaveat) {
 				defer wg.Done()
 				vlog.VI(3).Infof("Fetching discharge for %T from %v (%+v)", cav, cav.Location(), cav.Requirements())
-				call, err := c.StartCall(ctx, cav.Location(), "Discharge", []interface{}{cav, impetus(cav.Requirements(), server, method, args)}, opts...)
+				call, err := c.StartCall(ctx, cav.Location(), "Discharge", []interface{}{cav, filteredImpetus(cav.Requirements(), impetus)}, opts...)
 				if err != nil {
 					vlog.VI(3).Infof("Discharge fetch for caveat %T from %v failed: %v", cav, cav.Location(), err)
 					return
@@ -161,21 +170,21 @@
 	}
 }
 
-func impetus(r security.ThirdPartyRequirements, server security.PublicID, method string, args []interface{}) (impetus security.DischargeImpetus) {
-	if r.ReportServer {
-		names := server.Names()
-		impetus.Server = make([]security.BlessingPattern, len(names))
-		for i, n := range names {
-			impetus.Server[i] = security.BlessingPattern(n)
+// filteredImpetus returns a copy of 'before' after removing any values that are not required as per 'r'.
+func filteredImpetus(r security.ThirdPartyRequirements, before security.DischargeImpetus) (after security.DischargeImpetus) {
+	if r.ReportServer && len(before.Server) > 0 {
+		after.Server = make([]security.BlessingPattern, len(before.Server))
+		for i := range before.Server {
+			after.Server[i] = before.Server[i]
 		}
 	}
 	if r.ReportMethod {
-		impetus.Method = method
+		after.Method = before.Method
 	}
-	if r.ReportArguments {
-		impetus.Arguments = make([]vdlutil.Any, len(args))
-		for i, a := range args {
-			impetus.Arguments[i] = vdlutil.Any(a)
+	if r.ReportArguments && len(before.Arguments) > 0 {
+		after.Arguments = make([]vdlutil.Any, len(before.Arguments))
+		for i := range before.Arguments {
+			after.Arguments[i] = before.Arguments[i]
 		}
 	}
 	return
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index 1a10e49..ffc42b9 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -7,7 +7,7 @@
 	"testing"
 
 	_ "veyron.io/veyron/veyron/lib/testutil"
-	isecurity "veyron.io/veyron/veyron/runtimes/google/security"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
 
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
@@ -15,17 +15,21 @@
 	"veyron.io/veyron/veyron2/verror"
 )
 
-var testID = newID("test")
-
 // newTestFlows returns the two ends of a bidirectional flow.  Each end has its
 // own bookkeeping, to allow testing of method calls.
 func newTestFlows() (*testFlow, *testFlow) {
-	b0, b1 := new(bytes.Buffer), new(bytes.Buffer)
-	return &testFlow{r: b0, w: b1}, &testFlow{r: b1, w: b0}
+	var (
+		p0, p1               = sectest.NewPrincipal("p0"), sectest.NewPrincipal("p1")
+		blessing0, blessing1 = p0.BlessingStore().Default(), p1.BlessingStore().Default()
+		b0, b1               = new(bytes.Buffer), new(bytes.Buffer)
+	)
+	return &testFlow{r: b0, w: b1, p: p0, lb: blessing0, rb: blessing1}, &testFlow{r: b1, w: b0, p: p1, lb: blessing1, rb: blessing0}
 }
 
 type testFlow struct {
 	r, w          *bytes.Buffer
+	p             security.Principal
+	lb, rb        security.Blessings
 	numCloseCalls int
 	errClose      error
 }
@@ -34,11 +38,11 @@
 func (f *testFlow) Write(b []byte) (int, error)         { return f.w.Write(b) }
 func (f *testFlow) LocalEndpoint() naming.Endpoint      { return nil }
 func (f *testFlow) RemoteEndpoint() naming.Endpoint     { return nil }
-func (f *testFlow) LocalID() security.PublicID          { return testID.PublicID() }
-func (f *testFlow) RemoteID() security.PublicID         { return testID.PublicID() }
-func (f *testFlow) LocalPrincipal() security.Principal  { return nil }
-func (f *testFlow) LocalBlessings() security.Blessings  { return nil }
-func (f *testFlow) RemoteBlessings() security.Blessings { return nil }
+func (f *testFlow) LocalID() security.PublicID          { return nil }
+func (f *testFlow) RemoteID() security.PublicID         { return nil }
+func (f *testFlow) LocalPrincipal() security.Principal  { return f.p }
+func (f *testFlow) LocalBlessings() security.Blessings  { return f.lb }
+func (f *testFlow) RemoteBlessings() security.Blessings { return f.rb }
 func (f *testFlow) SetDeadline(<-chan struct{})         {}
 func (f *testFlow) IsClosed() bool                      { return false }
 func (f *testFlow) Closed() <-chan struct{}             { return nil }
@@ -56,7 +60,7 @@
 }
 
 func (td testDisp) Lookup(suffix, method string) (ipc.Invoker, security.Authorizer, error) {
-	return td.newInvoker(suffix), nil, nil
+	return td.newInvoker(suffix), testServerAuthorizer{}, nil
 }
 
 // closureInvoker serves a method with no user args or results:
@@ -122,7 +126,7 @@
 	}
 	for _, test := range tests {
 		clientFlow, serverFlow := newTestFlows()
-		client := newFlowClient(testContext(), clientFlow, nil, nil)
+		client := newFlowClient(testContext(), []string{"p0"}, clientFlow, nil, nil)
 		server := newFlowServer(serverFlow, ipcServer)
 		err := client.start(test.suffix, test.method, test.args, 0, nil)
 		if err != nil {
@@ -144,7 +148,3 @@
 		}
 	}
 }
-
-func init() {
-	isecurity.TrustIdentityProviders(testID)
-}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 5c4b391..b6c29e8 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -5,7 +5,6 @@
 	"fmt"
 	"io"
 	"net"
-	"os"
 	"reflect"
 	"strings"
 	"sync"
@@ -15,15 +14,14 @@
 	"veyron.io/veyron/veyron/lib/netstate"
 	_ "veyron.io/veyron/veyron/lib/testutil"
 	"veyron.io/veyron/veyron/lib/testutil/blackbox"
-	tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
 	"veyron.io/veyron/veyron/profiles"
 	imanager "veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/proxy"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/version"
 	"veyron.io/veyron/veyron/runtimes/google/lib/publisher"
 	inaming "veyron.io/veyron/veyron/runtimes/google/naming"
-	isecurity "veyron.io/veyron/veyron/runtimes/google/security"
 	tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
 	vsecurity "veyron.io/veyron/veyron/security"
 
@@ -39,11 +37,8 @@
 )
 
 var (
-	errAuthorizer = errors.New("ipc: application Authorizer denied access")
-	errMethod     = verror.Abortedf("server returned an error")
-	clientID      = newID("client")
-	serverID      = newID("server")
-	clock         = new(fakeClock)
+	errMethod = verror.Abortedf("server returned an error")
+	clock     = new(fakeClock)
 )
 
 type fakeClock struct {
@@ -92,12 +87,12 @@
 	return fmt.Sprintf("method:%q,suffix:%q,arg:%q", call.Method(), call.Suffix(), arg), u
 }
 
-func (*testServer) EchoIDs(call ipc.ServerCall) (server, client string) {
-	return fmt.Sprintf("%v", call.LocalID()), fmt.Sprintf("%v", call.RemoteID())
+func (*testServer) EchoBlessings(call ipc.ServerCall) (server, client string) {
+	return fmt.Sprintf("%v", call.LocalBlessings().ForContext(call)), fmt.Sprintf("%v", call.RemoteBlessings().ForContext(call))
 }
 
-func (*testServer) EchoBlessing(call ipc.ServerCall, arg string) (result, blessing string) {
-	return arg, fmt.Sprintf("%v", call.Blessing())
+func (*testServer) EchoGrantedBlessings(call ipc.ServerCall, arg string) (result, blessing string) {
+	return arg, fmt.Sprintf("%v", call.Blessings())
 }
 
 func (*testServer) EchoAndError(call ipc.ServerCall, arg string) (string, error) {
@@ -135,12 +130,11 @@
 	if !ok {
 		return nil, fmt.Errorf("discharger: unknown caveat(%T)", cav)
 	}
-	// Add a fakeTimeCaveat to allow the discharge to expire
-	expiry := fakeTimeCaveat(clock.Now())
 	if err := c.Dischargeable(ctx); err != nil {
 		return nil, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", c, err)
 	}
-	return serverID.MintDischarge(c, ctx, time.Hour, []security.Caveat{newCaveat(expiry)})
+	// Add a fakeTimeCaveat to be able to control discharge expiration via 'clock'.
+	return ctx.LocalPrincipal().MintDischarge(c, newCaveat(fakeTimeCaveat(clock.Now())))
 }
 
 type testServerAuthorizer struct{}
@@ -149,7 +143,7 @@
 	if c.Method() != "Unauthorized" {
 		return nil
 	}
-	return errAuthorizer
+	return fmt.Errorf("testServerAuthorizer denied access")
 }
 
 type testServerDisp struct{ server interface{} }
@@ -175,9 +169,9 @@
 	return ipc.ReflectInvoker(t.server), authorizer, nil
 }
 
-func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) (naming.Endpoint, ipc.Server) {
+func startServer(t *testing.T, principal security.Principal, sm stream.Manager, ns naming.Namespace, ts interface{}) (naming.Endpoint, ipc.Server) {
 	vlog.VI(1).Info("InternalNewServer")
-	server, err := InternalNewServer(testContext(), sm, ns, vc.FixedLocalID(serverID))
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{principal})
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
@@ -253,59 +247,21 @@
 	}
 }
 
-func createBundle(t *testing.T, clientID, serverID security.PrivateID, ts interface{}) (b bundle) {
+func createBundle(t *testing.T, client, server security.Principal, ts interface{}) (b bundle) {
 	b.sm = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	b.ns = tnaming.NewSimpleNamespace()
-	if serverID != nil {
-		b.ep, b.server = startServer(t, serverID, b.sm, b.ns, ts)
+	if server != nil {
+		b.ep, b.server = startServer(t, server, b.sm, b.ns, ts)
 	}
-	if clientID != nil {
+	if client != nil {
 		var err error
-		if b.client, err = InternalNewClient(b.sm, b.ns, vc.FixedLocalID(clientID)); err != nil {
+		if b.client, err = InternalNewClient(b.sm, b.ns, vc.LocalPrincipal{client}); err != nil {
 			t.Fatalf("InternalNewClient failed: %v", err)
 		}
 	}
 	return
 }
 
-func bless(blessor security.PrivateID, blessee security.PublicID, name string, caveats ...security.Caveat) security.PublicID {
-	blessed, err := blessor.Bless(blessee, name, 24*time.Hour, caveats)
-	if err != nil {
-		panic(err)
-	}
-	return blessed
-}
-
-func derive(blessor security.PrivateID, name string, caveats ...security.Caveat) security.PrivateID {
-	id := newID("irrelevant")
-	derivedID, err := id.Derive(bless(blessor, id.PublicID(), name, caveats...))
-	if err != nil {
-		panic(err)
-	}
-	return derivedID
-}
-
-// deriveForThirdPartyCaveats creates a SetPrivateID that can be used for
-//  1. talking to the server, if the caveats are fulfilled
-//  2. getting discharges, even if the caveats are not fulfilled
-// As an identity with an unfulfilled caveat is invalid (even for asking for  a
-// discharge), this function creates a set of two identities. The first will
-// have the caveats, the second will always be valid, but only for getting
-// discharges. The client presents both blessings in both cases, the discharger
-// ignores the first if it is invalid.
-func deriveForThirdPartyCaveats(blessor security.PrivateID, name string, caveats ...security.Caveat) security.PrivateID {
-	id := derive(blessor, name, caveats...)
-	dischargeID, err := id.Derive(bless(blessor, id.PublicID(), name, mkCaveat(security.MethodCaveat("Discharge"))))
-	if err != nil {
-		panic(err)
-	}
-	id, err = isecurity.NewSetPrivateID(id, dischargeID)
-	if err != nil {
-		panic(err)
-	}
-	return id
-}
-
 func matchesErrorPattern(err error, pattern string) bool {
 	if (len(pattern) == 0) != (err == nil) {
 		return false
@@ -316,7 +272,7 @@
 func TestMultipleCallsToServe(t *testing.T) {
 	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	ns := tnaming.NewSimpleNamespace()
-	server, err := InternalNewServer(testContext(), sm, ns, vc.FixedLocalID(serverID))
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{sectest.NewPrincipal()})
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
@@ -360,64 +316,62 @@
 	verifyMountMissing(t, ns, n3)
 }
 
-func TestStartCall(t *testing.T) {
+func TestRemoteIDCallOpt(t *testing.T) {
 	const (
-		authorizeErr = "not authorized because"
-		nameErr      = "does not match the provided pattern"
+		vcErr   = "VC handshake failed"
+		nameErr = "does not match the provided pattern"
 	)
 
 	var (
-		cavOnlyV1, _    = security.PeerBlessingsCaveat("client/v1")
-		cavExpired, _   = security.ExpiryCaveat(time.Now().Add(-1 * time.Second))
-		clientV1ID      = derive(clientID, "v1")
-		clientV2ID      = derive(clientID, "v2")
-		serverV1ID      = derive(serverID, "v1", cavOnlyV1)
-		serverExpiredID = derive(serverID, "expired", cavExpired)
+		pprovider, pclient, pserver = sectest.NewPrincipal("root"), sectest.NewPrincipal(), sectest.NewPrincipal()
+		bserver                     = bless(pprovider, pserver, "server")
+		bexpiredserver              = bless(pprovider, pserver, "server", mkCaveat(security.ExpiryCaveat(time.Now().Add(-1*time.Second))))
+
+		mgr   = imanager.InternalNew(naming.FixedRoutingID(0x1111111))
+		ns    = tnaming.NewSimpleNamespace()
+		tests = []struct {
+			server  security.Blessings       // blessings presented by the server to the client.
+			pattern security.BlessingPattern // pattern on the server identity expected by the client.
+			err     string
+		}{
+			// Client accepts talking to the server only if the server's identity matches the provided pattern
+			{bserver, security.AllPrincipals, ""},
+			{bserver, "root/server", ""},
+			{bserver, "root/otherserver", nameErr},
+			{bserver, "otherroot/server", nameErr},
+
+			// Client does not talk to a server that presents an expired identity.
+			{bexpiredserver, security.AllPrincipals, vcErr},
+		}
+		_, server = startServer(t, pserver, mgr, ns, &testServer{})
 	)
-
-	tests := []struct {
-		clientID, serverID security.PrivateID
-		pattern            security.BlessingPattern // pattern on the server identity expected by client.
-		err                string
-	}{
-		// Client accepts talking to server only if server's identity matches the
-		// provided pattern.
-		{clientID, serverID, security.AllPrincipals, ""},
-		{clientID, serverID, "server", ""},
-		{clientID, serverID, "server/v1", ""},
-		{clientID, serverID, "anotherServer", nameErr},
-
-		// All clients reject talking to a server with an expired identity.
-		{clientID, serverExpiredID, security.AllPrincipals, authorizeErr},
-		{clientV1ID, serverExpiredID, security.AllPrincipals, authorizeErr},
-		{clientV2ID, serverExpiredID, security.AllPrincipals, authorizeErr},
-
-		// Only clientV1 accepts talking to serverV1.
-		{clientV1ID, serverV1ID, security.AllPrincipals, ""},
-		{clientV2ID, serverV1ID, security.AllPrincipals, authorizeErr},
-	}
-	// Servers and clients will be created per-test, use the same stream manager and mounttable.
-	mgr := imanager.InternalNew(naming.FixedRoutingID(0x1111111))
-	ns := tnaming.NewSimpleNamespace()
+	defer stopServer(t, server, ns)
+	// Make the client and server principals trust root certificates from pprovider
+	pclient.AddToRoots(pprovider.BlessingStore().Default())
+	pserver.AddToRoots(pprovider.BlessingStore().Default())
+	// Create a blessing that the client is willing to share with server.
+	pclient.BlessingStore().Set(bless(pprovider, pclient, "client"), "root/server")
 	for _, test := range tests {
-		name := fmt.Sprintf("(clientID:%q serverID:%q)", test.clientID, test.serverID)
-		_, server := startServer(t, test.serverID, mgr, ns, &testServer{})
-		client, err := InternalNewClient(mgr, ns, vc.FixedLocalID(test.clientID))
+		name := fmt.Sprintf("(%q@%q)", test.pattern, test.server)
+		pserver.BlessingStore().SetDefault(test.server)
+		// Recreate client in each test (so as to not re-use VCs to the server).
+		client, err := InternalNewClient(mgr, ns, vc.LocalPrincipal{pclient})
 		if err != nil {
-			t.Errorf("%s: Client creation failed: %v", name, err)
-			stopServer(t, server, ns)
+			t.Errorf("%s: failed ot create client: %v", name, err)
 			continue
 		}
-		if call, err := client.StartCall(testContext(), "mountpoint/server/suffix", "irrelevant", nil, veyron2.RemoteID(test.pattern)); !matchesErrorPattern(err, test.err) {
+		if call, err := client.StartCall(testContext(), "mountpoint/server/suffix", "Method", nil, veyron2.RemoteID(test.pattern)); !matchesErrorPattern(err, test.err) {
 			t.Errorf(`%s: client.StartCall: got error "%v", want to match "%v"`, name, err, test.err)
 		} else if call != nil {
-			serverBlessings, _ := call.RemoteBlessings()
-			if !reflect.DeepEqual(serverBlessings, test.serverID.PublicID().Names()) {
-				t.Errorf("%s: Server authenticated as %v, wanted %v", name, serverBlessings, test.serverID.PublicID().Names())
+			blessings, proof := call.RemoteBlessings()
+			if proof == nil {
+				t.Errorf("%s: Returned nil for remote blessings", name)
+			}
+			if !test.pattern.MatchedBy(blessings...) {
+				t.Errorf("%s: %q.MatchedBy(%v) failed", name, test.pattern, blessings)
 			}
 		}
 		client.Close()
-		stopServer(t, server, ns)
 	}
 }
 
@@ -442,26 +396,34 @@
 		results    v
 		finishErr  error
 	}
-	tests := []testcase{
-		{"mountpoint/server/suffix", "Closure", nil, nil, nil, nil, nil},
-		{"mountpoint/server/suffix", "Error", nil, nil, nil, v{errMethod}, nil},
+	var (
+		tests = []testcase{
+			{"mountpoint/server/suffix", "Closure", nil, nil, nil, nil, nil},
+			{"mountpoint/server/suffix", "Error", nil, nil, nil, v{errMethod}, nil},
 
-		{"mountpoint/server/suffix", "Echo", v{"foo"}, nil, nil, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, nil},
-		{"mountpoint/server/suffix/abc", "Echo", v{"bar"}, nil, nil, v{`method:"Echo",suffix:"suffix/abc",arg:"bar"`}, nil},
+			{"mountpoint/server/suffix", "Echo", v{"foo"}, nil, nil, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, nil},
+			{"mountpoint/server/suffix/abc", "Echo", v{"bar"}, nil, nil, v{`method:"Echo",suffix:"suffix/abc",arg:"bar"`}, nil},
 
-		{"mountpoint/server/suffix", "EchoUser", v{"foo", userType("bar")}, nil, nil, v{`method:"EchoUser",suffix:"suffix",arg:"foo"`, userType("bar")}, nil},
-		{"mountpoint/server/suffix/abc", "EchoUser", v{"baz", userType("bla")}, nil, nil, v{`method:"EchoUser",suffix:"suffix/abc",arg:"baz"`, userType("bla")}, nil},
-		{"mountpoint/server/suffix", "Stream", v{"foo"}, v{userType("bar"), userType("baz")}, nil, v{`method:"Stream",suffix:"suffix",arg:"foo" bar baz`, nil}, nil},
-		{"mountpoint/server/suffix/abc", "Stream", v{"123"}, v{userType("456"), userType("789")}, nil, v{`method:"Stream",suffix:"suffix/abc",arg:"123" 456 789`, nil}, nil},
-		{"mountpoint/server/suffix", "EchoIDs", nil, nil, nil, v{"server", "client"}, nil},
-		{"mountpoint/server/suffix", "EchoAndError", v{"bugs bunny"}, nil, nil, v{`method:"EchoAndError",suffix:"suffix",arg:"bugs bunny"`, nil}, nil},
-		{"mountpoint/server/suffix", "EchoAndError", v{"error"}, nil, nil, v{`method:"EchoAndError",suffix:"suffix",arg:"error"`, errMethod}, nil},
-	}
-	name := func(t testcase) string {
-		return fmt.Sprintf("%s.%s(%v)", t.name, t.method, t.args)
-	}
-	b := createBundle(t, clientID, serverID, &testServer{})
+			{"mountpoint/server/suffix", "EchoUser", v{"foo", userType("bar")}, nil, nil, v{`method:"EchoUser",suffix:"suffix",arg:"foo"`, userType("bar")}, nil},
+			{"mountpoint/server/suffix/abc", "EchoUser", v{"baz", userType("bla")}, nil, nil, v{`method:"EchoUser",suffix:"suffix/abc",arg:"baz"`, userType("bla")}, nil},
+			{"mountpoint/server/suffix", "Stream", v{"foo"}, v{userType("bar"), userType("baz")}, nil, v{`method:"Stream",suffix:"suffix",arg:"foo" bar baz`, nil}, nil},
+			{"mountpoint/server/suffix/abc", "Stream", v{"123"}, v{userType("456"), userType("789")}, nil, v{`method:"Stream",suffix:"suffix/abc",arg:"123" 456 789`, nil}, nil},
+			{"mountpoint/server/suffix", "EchoBlessings", nil, nil, nil, v{"[server]", "[client]"}, nil},
+			{"mountpoint/server/suffix", "EchoAndError", v{"bugs bunny"}, nil, nil, v{`method:"EchoAndError",suffix:"suffix",arg:"bugs bunny"`, nil}, nil},
+			{"mountpoint/server/suffix", "EchoAndError", v{"error"}, nil, nil, v{`method:"EchoAndError",suffix:"suffix",arg:"error"`, errMethod}, nil},
+		}
+		name = func(t testcase) string {
+			return fmt.Sprintf("%s.%s(%v)", t.name, t.method, t.args)
+		}
+
+		pserver = sectest.NewPrincipal("server")
+		pclient = sectest.NewPrincipal("client")
+
+		b = createBundle(t, pclient, pserver, &testServer{})
+	)
 	defer b.cleanup(t)
+	// The server needs to recognize the client's root certificate.
+	pserver.AddToRoots(pclient.BlessingStore().Default())
 	for _, test := range tests {
 		vlog.VI(1).Infof("%s client.StartCall", name(test))
 		call, err := b.client.StartCall(testContext(), test.name, test.method, test.args)
@@ -510,7 +472,7 @@
 
 func TestMultipleFinish(t *testing.T) {
 	type v []interface{}
-	b := createBundle(t, clientID, serverID, &testServer{})
+	b := createBundle(t, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"), &testServer{})
 	defer b.cleanup(t)
 	call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "Echo", v{"foo"})
 	if err != nil {
@@ -528,30 +490,34 @@
 	}
 }
 
-// granter implements ipc.Granter, returning a fixed (security.PublicID, error) pair.
+// granter implements ipc.Granter, returning a fixed (security.Blessings, error) pair.
 type granter struct {
 	ipc.CallOpt
-	id  security.PublicID
+	b   security.Blessings
 	err error
 }
 
-func (g granter) Grant(id security.PublicID) (security.PublicID, error) { return g.id, g.err }
+func (g granter) Grant(id security.Blessings) (security.Blessings, error) { return g.b, g.err }
 
-func TestBlessing(t *testing.T) {
-	b := createBundle(t, clientID, serverID, &testServer{})
+func TestGranter(t *testing.T) {
+	var (
+		pclient = sectest.NewPrincipal("client")
+		pserver = sectest.NewPrincipal("server")
+		b       = createBundle(t, pclient, pserver, &testServer{})
+	)
 	defer b.cleanup(t)
 
 	tests := []struct {
-		granter                       ipc.CallOpt
+		granter                       ipc.Granter
 		blessing, starterr, finisherr string
 	}{
 		{blessing: "<nil>"},
-		{granter: granter{id: bless(clientID, serverID.PublicID(), "blessed")}, blessing: "client/blessed"},
+		{granter: granter{b: bless(pclient, pserver, "blessed")}, blessing: "client/blessed(0 caveats)"},
 		{granter: granter{err: errors.New("hell no")}, starterr: "hell no"},
-		{granter: granter{id: clientID.PublicID()}, finisherr: "blessing provided not bound to this server"},
+		{granter: granter{b: pclient.BlessingStore().Default()}, finisherr: "blessing granted not bound to this server"},
 	}
 	for _, test := range tests {
-		call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "EchoBlessing", []interface{}{"argument"}, test.granter)
+		call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "EchoGrantedBlessings", []interface{}{"argument"}, test.granter)
 		if !matchesErrorPattern(err, test.starterr) {
 			t.Errorf("%+v: StartCall returned error %v", test, err)
 		}
@@ -571,8 +537,8 @@
 	}
 }
 
-func mkThirdPartyCaveat(discharger security.PublicID, location string, c security.Caveat) security.Caveat {
-	tpc, err := security.NewPublicKeyCaveat(discharger.PublicKey(), location, security.ThirdPartyRequirements{}, c)
+func mkThirdPartyCaveat(discharger security.PublicKey, location string, c security.Caveat) security.Caveat {
+	tpc, err := security.NewPublicKeyCaveat(discharger, location, security.ThirdPartyRequirements{}, c)
 	if err != nil {
 		panic(err)
 	}
@@ -585,7 +551,7 @@
 
 // Implements ipc.Dispatcher
 func (s *dischargeImpetusTester) Lookup(_, _ string) (ipc.Invoker, security.Authorizer, error) {
-	return ipc.ReflectInvoker(s), nil, nil
+	return ipc.ReflectInvoker(s), testServerAuthorizer{}, nil
 }
 
 // Implements the discharge service: Always fails to issue a discharge, but records the impetus
@@ -604,20 +570,31 @@
 
 func TestDischargeImpetus(t *testing.T) {
 	var (
-		// The Discharge service can be run by anyone, but in these tests it is the same as the server.
-		dischargerID = serverID.PublicID()
+		pserver     = sectest.NewPrincipal("server")
+		pdischarger = pserver // In general, the discharger can be a separate principal. In this test, it happens to be the server.
+		sm          = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
+		ns          = tnaming.NewSimpleNamespace()
 
-		mkClientID = func(req security.ThirdPartyRequirements) security.PrivateID {
-			tpc, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), "mountpoint/discharger", req, newCaveat(alwaysValidCaveat{}))
+		mkClient = func(req security.ThirdPartyRequirements) vc.LocalPrincipal {
+			pclient := sectest.NewPrincipal()
+			tpc, err := security.NewPublicKeyCaveat(pdischarger.PublicKey(), "mountpoint/discharger", req, security.UnconstrainedUse())
 			if err != nil {
-				t.Fatalf("Failed to create ThirdPartyCaveat: %v", err)
+				t.Fatalf("Failed to create ThirdPartyCaveat(%+v): %v", req, err)
 			}
-			return deriveForThirdPartyCaveats(serverID, "client", newCaveat(tpc))
+			cav, err := security.NewCaveat(tpc)
+			if err != nil {
+				t.Fatal(err)
+			}
+			b, err := pclient.BlessSelf("client", cav)
+			if err != nil {
+				t.Fatalf("BlessSelf failed: %v", err)
+			}
+			pclient.AddToRoots(pserver.BlessingStore().Default()) // make the client recognize the server.
+			pclient.BlessingStore().Set(b, "server")
+			return vc.LocalPrincipal{pclient}
 		}
 	)
-	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
-	ns := tnaming.NewSimpleNamespace()
-	server, err := InternalNewServer(testContext(), sm, ns, vc.FixedLocalID(serverID))
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{pserver})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -641,7 +618,7 @@
 		},
 		{ // Require everything
 			Requirements: security.ThirdPartyRequirements{ReportServer: true, ReportMethod: true, ReportArguments: true},
-			Impetus:      security.DischargeImpetus{Server: names2patterns(serverID.PublicID().Names()), Method: "Method", Arguments: []vdlutil.Any{vdlutil.Any("argument")}},
+			Impetus:      security.DischargeImpetus{Server: []security.BlessingPattern{"server"}, Method: "Method", Arguments: []vdlutil.Any{vdlutil.Any("argument")}},
 		},
 		{ // Require only the method name
 			Requirements: security.ThirdPartyRequirements{ReportMethod: true},
@@ -650,7 +627,7 @@
 	}
 
 	for _, test := range tests {
-		client, err := InternalNewClient(sm, ns, vc.FixedLocalID(mkClientID(test.Requirements)))
+		client, err := InternalNewClient(sm, ns, mkClient(test.Requirements))
 		if err != nil {
 			t.Fatalf("InternalNewClient(%+v) failed: %v", test.Requirements, err)
 		}
@@ -668,31 +645,33 @@
 
 func TestRPCAuthorization(t *testing.T) {
 	var (
-		now = time.Now()
-		// First-party caveats
-		cavOnlyEcho = mkCaveat(security.MethodCaveat("Echo"))
-		cavExpired  = mkCaveat(security.ExpiryCaveat(now))
-		// Third-party caveats
-		// The Discharge service can be run by any identity, but in our tests the same server runs
-		// a Discharge service as well.
-		dischargerID = serverID.PublicID()
-		cavTPValid   = mkThirdPartyCaveat(dischargerID, "mountpoint/server/discharger", mkCaveat(security.ExpiryCaveat(now.Add(24*time.Hour))))
-		cavTPExpired = mkThirdPartyCaveat(dischargerID, "mountpoint/server/discharger", mkCaveat(security.ExpiryCaveat(now)))
+		// Principals
+		pclient     = sectest.NewPrincipal("client")
+		pserver     = sectest.NewPrincipal("server")
+		pdischarger = pserver
 
-		// Client blessings that will be tested
-		blessedByServerOnlyEcho  = derive(serverID, "onlyEcho", cavOnlyEcho)
-		blessedByServerExpired   = derive(serverID, "expired", cavExpired)
-		blessedByServerTPValid   = deriveForThirdPartyCaveats(serverID, "tpvalid", cavTPValid)
-		blessedByServerTPExpired = deriveForThirdPartyCaveats(serverID, "tpexpired", cavTPExpired)
-		blessedByClient          = derive(clientID, "blessed")
+		now = time.Now()
+
+		// Caveats on blessings to the client: First-party caveats
+		cavOnlyEcho = mkCaveat(security.MethodCaveat("Echo"))
+		cavExpired  = mkCaveat(security.ExpiryCaveat(now.Add(-1 * time.Second)))
+		// Caveats on blessings to the client: Third-party caveats
+		cavTPValid   = mkThirdPartyCaveat(pdischarger.PublicKey(), "mountpoint/server/discharger", mkCaveat(security.ExpiryCaveat(now.Add(24*time.Hour))))
+		cavTPExpired = mkThirdPartyCaveat(pdischarger.PublicKey(), "mountpoint/server/discharger", mkCaveat(security.ExpiryCaveat(now.Add(-1*time.Second))))
+
+		// Client blessings that will be tested.
+		bServerClientOnlyEcho  = bless(pserver, pclient, "onlyecho", cavOnlyEcho)
+		bServerClientExpired   = bless(pserver, pclient, "expired", cavExpired)
+		bServerClientTPValid   = bless(pserver, pclient, "dischargeable_third_party_caveat", cavTPValid)
+		bServerClientTPExpired = bless(pserver, pclient, "expired_third_party_caveat", cavTPExpired)
+		bClient                = pclient.BlessingStore().Default()
+		bRandom, _             = pclient.BlessSelf("random")
 	)
-	const (
-		expiredIDErr = "security.unixTimeExpiryCaveat"
-		aclAuthErr   = "no matching ACL entry found"
-	)
-	invalidMethodErr := func(method string) string {
-		return fmt.Sprintf(`security.methodCaveat=[Echo] fails validation for method %q`, method)
-	}
+	// The server should recognize the client principal as an authority on "client/..." and "random/..." blessings.
+	pserver.AddToRoots(bClient)
+	pserver.AddToRoots(bRandom)
+	// And the client needs to recognize the server's blessing to decide which of its own blessings to share.
+	pclient.AddToRoots(pserver.BlessingStore().Default())
 
 	type v []interface{}
 	type testcase struct {
@@ -703,82 +682,112 @@
 		results   v
 		finishErr string
 	}
-	tests := []testcase{
-		// Clients whose identities have invalid caveats are not by authorized by any authorizer.
-		{blessedByServerExpired, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, expiredIDErr},
-		{blessedByServerExpired, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, expiredIDErr},
-		{blessedByServerOnlyEcho, "mountpoint/server/nilAuth", "Closure", nil, nil, invalidMethodErr("Closure")},
-		{blessedByServerOnlyEcho, "mountpoint/server/suffix", "Closure", nil, nil, invalidMethodErr("Closure")},
-		// Only clients with a trusted name that matches either the server's identity or an identity blessed
-		// by the server are authorized by the (default) nilAuth authorizer.
-		{clientID, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, aclAuthErr},
-		{blessedByClient, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, aclAuthErr},
-		{serverID, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"nilAuth",arg:"foo"`}, ""},
-		{serverID, "mountpoint/server/nilAuth", "Closure", nil, nil, ""},
-		{blessedByServerOnlyEcho, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"nilAuth",arg:"foo"`}, ""},
-		// Only clients matching the server's ACL are authorized.
-		{clientID, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"aclAuth",arg:"foo"`}, ""},
-		{blessedByClient, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, aclAuthErr},
-		{serverID, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"aclAuth",arg:"foo"`}, ""},
-		{blessedByServerOnlyEcho, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"aclAuth",arg:"foo"`}, ""},
-		{clientID, "mountpoint/server/aclAuth", "Closure", nil, nil, ""},
-		{blessedByClient, "mountpoint/server/aclAuth", "Closure", nil, nil, aclAuthErr},
-		{serverID, "mountpoint/server/aclAuth", "Closure", nil, nil, ""},
-		// All methods except "Unauthorized" are authorized by the custom authorizer.
-		{clientID, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
-		{blessedByClient, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
-		{serverID, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
-		{blessedByServerOnlyEcho, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
-		{clientID, "mountpoint/server/suffix", "Closure", nil, nil, ""},
-		{blessedByClient, "mountpoint/server/suffix", "Closure", nil, nil, ""},
-		{serverID, "mountpoint/server/suffix", "Closure", nil, nil, ""},
-		{clientID, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, "application Authorizer denied access"},
-		{blessedByClient, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, "application Authorizer denied access"},
-		{serverID, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, "application Authorizer denied access"},
-		// Third-party caveat discharges should be fetched and forwarded
-		{blessedByServerTPValid, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
-		{blessedByServerTPExpired, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, "missing discharge"},
-	}
-	name := func(t testcase) string {
-		return fmt.Sprintf("%q RPCing %s.%s(%v)", t.clientID.PublicID(), t.name, t.method, t.args)
-	}
+	tests := []struct {
+		blessings  security.Blessings // Blessings used by the client
+		name       string             // object name on which the method is invoked
+		method     string
+		args       v
+		results    v
+		authorized bool // Whether or not the RPC should be authorized by the server.
+	}{
+		// There are three different authorization policies (security.Authorizer implementations)
+		// used by the server, depending on the suffix (see testServerDisp.Lookup):
+		// - nilAuth suffix: the default authorization policy (only delegates of or delegators of the server can call RPCs)
+		// - aclAuth suffix: the ACL only allows "server/..." or "client"
+		// - other suffixes: testServerAuthorizer allows any principal to call any method except "Unauthorized"
 
-	b := createBundle(t, nil, serverID, &testServer{}) // we only create the server, a separate client will be created for each test.
+		// Expired blessings should fail nilAuth and aclAuth (which care about names), but should succeed on
+		// other suffixes (which allow all blessings), unless calling the Unauthorized method.
+		{bServerClientExpired, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, false},
+		{bServerClientExpired, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, false},
+		{bServerClientExpired, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientExpired, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, false},
+
+		// Same for blessings that should fail to obtain a discharge for the third party caveat.
+		{bServerClientTPExpired, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, false},
+		{bServerClientTPExpired, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, false},
+		{bServerClientTPExpired, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientTPExpired, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, false},
+
+		// The "server/client" blessing (with MethodCaveat("Echo")) should satisfy all authorization policies
+		// when "Echo" is called.
+		{bServerClientOnlyEcho, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientOnlyEcho, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientOnlyEcho, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, true},
+
+		// The "server/client" blessing (with MethodCaveat("Echo")) should satisfy no authorization policy
+		// when any other method is invoked, except for the testServerAuthorizer policy (which will
+		// not recognize the blessing "server/onlyecho", but it would authorize anyone anyway).
+		{bServerClientOnlyEcho, "mountpoint/server/nilAuth", "Closure", nil, nil, false},
+		{bServerClientOnlyEcho, "mountpoint/server/aclAuth", "Closure", nil, nil, false},
+		{bServerClientOnlyEcho, "mountpoint/server/suffix", "Closure", nil, nil, true},
+
+		// The "client" blessing doesn't satisfy the default authorization policy, but does satisfy
+		// the ACL and the testServerAuthorizer policy.
+		{bClient, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, false},
+		{bClient, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, true},
+		{bClient, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, true},
+		{bClient, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, false},
+
+		// The "random" blessing does not satisfy either the default policy or the ACL, but does
+		// satisfy testServerAuthorizer.
+		{bRandom, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, false},
+		{bRandom, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, false},
+		{bRandom, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, true},
+		{bRandom, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, false},
+
+		// The "server/dischargeable_third_party_caveat" blessing satisfies all policies.
+		// (the discharges should be fetched).
+		{bServerClientTPValid, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientTPValid, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientTPValid, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, true},
+		{bServerClientTPValid, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, false},
+	}
+	b := createBundle(t, nil, pserver, &testServer{}) // we only create the server, a separate client will be created for each test.
 	defer b.cleanup(t)
 	for _, test := range tests {
-		client, err := InternalNewClient(b.sm, b.ns, vc.FixedLocalID(test.clientID))
+		name := fmt.Sprintf("%q.%s(%v) by %v", test.name, test.method, test.args, test.blessings)
+		client, err := InternalNewClient(b.sm, b.ns, vc.LocalPrincipal{pclient})
 		if err != nil {
 			t.Fatalf("InternalNewClient failed: %v", err)
 		}
 		defer client.Close()
+		pclient.BlessingStore().Set(test.blessings, "server")
 		call, err := client.StartCall(testContext(), test.name, test.method, test.args)
 		if err != nil {
-			t.Errorf(`%s client.StartCall got unexpected error: "%v"`, name(test), err)
+			t.Errorf(`%s client.StartCall got unexpected error: "%v"`, name, err)
 			continue
 		}
 		results := makeResultPtrs(test.results)
 		err = call.Finish(results...)
-		if !matchesErrorPattern(err, test.finishErr) {
-			t.Errorf(`%s call.Finish got error: "%v", want to match: "%v"`, name(test), err, test.finishErr)
+		if err != nil && test.authorized {
+			t.Errorf(`%s call.Finish got error: "%v", wanted the RPC to succeed`, name, err)
+		} else if err == nil && !test.authorized {
+			t.Errorf("%s call.Finish succeeded, expected authorization failure", name)
+		} else if !test.authorized && !verror.Is(err, verror.NoAccess) {
+			t.Errorf("%s. call.Finish returned error %v(%v), wanted %v", name, verror.Convert(err).ErrorID(), err, verror.NoAccess)
 		}
 	}
 }
 
-type alwaysValidCaveat struct{}
-
-func (alwaysValidCaveat) Validate(security.Context) error { return nil }
-
 func TestDischargePurgeFromCache(t *testing.T) {
 	var (
-		dischargerID = serverID.PublicID()
-		c            = mkThirdPartyCaveat(dischargerID, "mountpoint/server/discharger", newCaveat(alwaysValidCaveat{}))
-		clientCID    = deriveForThirdPartyCaveats(serverID, "client", c)
+		pserver     = sectest.NewPrincipal("server")
+		pdischarger = pserver // In general, the discharger can be a separate principal. In this test, it happens to be the server.
+		pclient     = sectest.NewPrincipal("client")
+		// Client is blessed with a third-party caveat. The discharger service issues discharges with a fakeTimeCaveat.
+		// This blessing is presented to "server".
+		bclient = bless(pserver, pclient, "client", mkThirdPartyCaveat(pdischarger.PublicKey(), "mountpoint/server/discharger", security.UnconstrainedUse()))
 	)
-	b := createBundle(t, clientCID, serverID, &testServer{})
+	// Setup the client to recognize the server's blessing and present bclient to it.
+	pclient.AddToRoots(pserver.BlessingStore().Default())
+	pclient.BlessingStore().Set(bclient, "server")
+
+	b := createBundle(t, pclient, pserver, &testServer{})
 	defer b.cleanup(t)
 
 	call := func() error {
-		call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "Echo", []interface{}{"batman"})
+		call, err := b.client.StartCall(testContext(), "mountpoint/server/aclAuth", "Echo", []interface{}{"batman"})
 		if err != nil {
 			return fmt.Errorf("client.StartCall failed: %v", err)
 		}
@@ -786,7 +795,7 @@
 		if err := call.Finish(&got); err != nil {
 			return fmt.Errorf("client.Finish failed: %v", err)
 		}
-		if want := `method:"Echo",suffix:"suffix",arg:"batman"`; got != want {
+		if want := `method:"Echo",suffix:"aclAuth",arg:"batman"`; got != want {
 			return fmt.Errorf("Got [%v] want [%v]", got, want)
 		}
 		return nil
@@ -798,8 +807,8 @@
 	}
 	// Advance virtual clock, which will invalidate the discharge
 	clock.Advance(1)
-	if err := call(); !matchesErrorPattern(err, "fakeTimeCaveat expired") {
-		t.Errorf("Got error [%v] wanted to match pattern 'fakeTimeCaveat expired'", err)
+	if err, want := call(), "not authorized"; !matchesErrorPattern(err, want) {
+		t.Errorf("Got error [%v] wanted to match pattern %q", err, want)
 	}
 	// But retrying will succeed since the discharge should be purged from cache and refreshed
 	if err := call(); err != nil {
@@ -851,7 +860,7 @@
 // TestCancel tests cancellation while the server is reading from a stream.
 func TestCancel(t *testing.T) {
 	ts := newCancelTestServer(t)
-	b := createBundle(t, clientID, serverID, ts)
+	b := createBundle(t, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"), ts)
 	defer b.cleanup(t)
 
 	call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "CancelStreamReader", []interface{}{})
@@ -865,7 +874,7 @@
 // the server is not reading that the cancel message gets through.
 func TestCancelWithFullBuffers(t *testing.T) {
 	ts := newCancelTestServer(t)
-	b := createBundle(t, clientID, serverID, ts)
+	b := createBundle(t, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"), ts)
 	defer b.cleanup(t)
 
 	call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "CancelStreamIgnorer", []interface{}{})
@@ -901,7 +910,7 @@
 
 func TestStreamReadTerminatedByServer(t *testing.T) {
 	s := &streamRecvInGoroutineServer{c: make(chan error, 1)}
-	b := createBundle(t, clientID, serverID, s)
+	b := createBundle(t, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"), s)
 	defer b.cleanup(t)
 
 	call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "RecvInGoroutine", []interface{}{})
@@ -933,7 +942,7 @@
 
 // TestConnectWithIncompatibleServers tests that clients ignore incompatible endpoints.
 func TestConnectWithIncompatibleServers(t *testing.T) {
-	b := createBundle(t, clientID, serverID, &testServer{})
+	b := createBundle(t, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"), &testServer{})
 	defer b.cleanup(t)
 
 	// Publish some incompatible endpoints.
@@ -971,10 +980,9 @@
 // connection to the server if the server dies and comes back (on the same
 // endpoint).
 func TestReconnect(t *testing.T) {
-	b := createBundle(t, clientID, nil, nil) // We only need the client from the bundle.
+	b := createBundle(t, sectest.NewPrincipal("client"), nil, nil) // We only need the client from the bundle.
 	defer b.cleanup(t)
-	idFile := tsecurity.SaveIdentityToFile(derive(clientID, "server"))
-	server := blackbox.HelperCommand(t, "runServer", "127.0.0.1:0", idFile)
+	server := blackbox.HelperCommand(t, "runServer", "127.0.0.1:0")
 	server.Cmd.Start()
 	addr, err := server.ReadLineFromChild()
 	if err != nil {
@@ -1007,7 +1015,7 @@
 	}
 	// Resurrect the server with the same address, verify client
 	// re-establishes the connection.
-	server = blackbox.HelperCommand(t, "runServer", addr, idFile)
+	server = blackbox.HelperCommand(t, "runServer", addr)
 	defer server.Cleanup()
 	server.Cmd.Start()
 	if addr2, err := server.ReadLineFromChild(); addr2 != addr || err != nil {
@@ -1027,7 +1035,7 @@
 		a.IP = net.ParseIP("1.1.1.1")
 		return []ipc.Address{&netstate.AddrIfc{Addr: a}}, nil
 	}
-	server, err := InternalNewServer(testContext(), sm, ns, vc.FixedLocalID(serverID))
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{sectest.NewPrincipal("server")})
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
@@ -1063,7 +1071,7 @@
 	paerr := func(_ string, a []ipc.Address) ([]ipc.Address, error) {
 		return nil, fmt.Errorf("oops")
 	}
-	server, err := InternalNewServer(testContext(), sm, ns, vc.FixedLocalID(serverID))
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{sectest.NewPrincipal("server")})
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
@@ -1120,12 +1128,12 @@
 func TestProxy(t *testing.T) {
 	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	ns := tnaming.NewSimpleNamespace()
-	client, err := InternalNewClient(sm, ns, vc.FixedLocalID(clientID))
+	client, err := InternalNewClient(sm, ns, vc.LocalPrincipal{sectest.NewPrincipal("client")})
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer client.Close()
-	server, err := InternalNewServer(testContext(), sm, ns, vc.FixedLocalID(serverID))
+	server, err := InternalNewServer(testContext(), sm, ns, vc.LocalPrincipal{sectest.NewPrincipal("server")})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1191,25 +1199,10 @@
 	}
 }
 
-func loadIdentityFromFile(file string) security.PrivateID {
-	f, err := os.Open(file)
-	if err != nil {
-		vlog.Fatalf("failed to open %v: %v", file, err)
-	}
-	id, err := vsecurity.LoadIdentity(f)
-	f.Close()
-	if err != nil {
-		vlog.Fatalf("Failed to load identity from %v: %v", file, err)
-	}
-	return id
-}
-
 func runServer(argv []string) {
 	mgr := imanager.InternalNew(naming.FixedRoutingID(0x1111111))
 	ns := tnaming.NewSimpleNamespace()
-	id := loadIdentityFromFile(argv[1])
-	isecurity.TrustIdentityProviders(id)
-	server, err := InternalNewServer(testContext(), mgr, ns, vc.FixedLocalID(id))
+	server, err := InternalNewServer(testContext(), mgr, ns, vc.LocalPrincipal{sectest.NewPrincipal("server")})
 	if err != nil {
 		vlog.Fatalf("InternalNewServer failed: %v", err)
 	}
@@ -1233,7 +1226,7 @@
 	if err != nil {
 		vlog.Fatal(err)
 	}
-	proxy, err := proxy.New(rid, nil, "tcp", "127.0.0.1:0", "")
+	proxy, err := proxy.New(rid, sectest.NewPrincipal("proxy"), "tcp", "127.0.0.1:0", "")
 	if err != nil {
 		vlog.Fatal(err)
 	}
@@ -1247,12 +1240,8 @@
 }
 
 func init() {
-	isecurity.TrustIdentityProviders(clientID)
-	isecurity.TrustIdentityProviders(serverID)
-
 	blackbox.CommandTable["runServer"] = runServer
 	blackbox.CommandTable["runProxy"] = runProxy
 
 	vom.Register(fakeTimeCaveat(0))
-	vom.Register(alwaysValidCaveat{})
 }
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index a8ff3b3..36bda51 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -565,7 +565,7 @@
 	// authorizedRemoteID is the PublicID obtained after authorizing the remoteID
 	// of the underlying flow for the current request context.
 	authorizedRemoteID security.PublicID
-	blessing           security.PublicID
+	blessings          security.Blessings
 	method, suffix     string
 	label              security.Label
 	discharges         map[string]security.Discharge
@@ -620,15 +620,18 @@
 	return v
 }
 
-func defaultACL(id security.PublicID) security.ACL {
-	if id == nil {
-		return security.ACL{}
+func defaultAuthorizer(ctx security.Context) security.Authorizer {
+	var blessings []string
+	if ctx.LocalBlessings() == nil { // TODO(ashankar): This will go away once the old security model is removed
+		blessings = ctx.LocalID().Names()
+	} else {
+		blessings = ctx.LocalBlessings().ForContext(ctx)
 	}
-	in := map[security.BlessingPattern]security.LabelSet{}
-	for _, n := range id.Names() {
-		in[security.BlessingPattern(n+security.ChainSeparator+string(security.AllPrincipals))] = security.AllLabels
+	acl := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
+	for _, b := range blessings {
+		acl.In[security.BlessingPattern(b).MakeGlob()] = security.AllLabels
 	}
-	return security.ACL{In: in}
+	return vsecurity.NewACLAuthorizer(acl)
 }
 
 func (fs *flowServer) serve() error {
@@ -730,18 +733,17 @@
 	}()
 
 	// If additional credentials are provided, make them available in the context
-	if req.HasBlessing {
-		if err := fs.dec.Decode(&fs.blessing); err != nil {
-			return nil, verror.BadProtocolf("ipc: blessing decoding failed: %v", err)
-		}
-		// Detect unusable blessings now, rather then discovering they are unusable on first use.
-		if !reflect.DeepEqual(fs.blessing.PublicKey(), fs.flow.LocalID().PublicKey()) {
-			return nil, verror.BadProtocolf("ipc: blessing provided not bound to this server")
-		}
-		// TODO(ashankar,ataly): Potential confused deputy attack: The client provides the
-		// server's identity as the blessing. Figure out what we want to do about this -
-		// should servers be able to assume that a blessing is something that does not
-		// have the authorizations that the server's own identity has?
+	var err error
+	if fs.blessings, err = security.NewBlessings(req.GrantedBlessings); err != nil {
+		return nil, verror.BadProtocolf("ipc: failed to decode granted blessings: %v", err)
+	}
+	// Detect unusable blessings now, rather then discovering they are unusable on first use.
+	// TODO(ashankar,ataly): Potential confused deputy attack: The client provides the
+	// server's identity as the blessing. Figure out what we want to do about this -
+	// should servers be able to assume that a blessing is something that does not
+	// have the authorizations that the server's own identity has?
+	if fs.blessings != nil && !reflect.DeepEqual(fs.blessings.PublicKey(), fs.flow.LocalPrincipal().PublicKey()) {
+		return nil, verror.BadProtocolf("ipc: blessing granted not bound to this server(%v vs %v)", fs.blessings.PublicKey(), fs.flow.LocalPrincipal().PublicKey())
 	}
 	// Receive third party caveat discharges the client sent
 	for i := uint64(0); i < req.NumDischarges; i++ {
@@ -772,8 +774,8 @@
 			return nil, verror.BadProtocolf("ipc: arg %d decoding failed: %v", ix, err)
 		}
 	}
-	// Authorize the PublicID at the remote end of the flow for the request context.
 	if remoteID := fs.flow.RemoteID(); remoteID != nil {
+		// TODO(ashankar): This whole check goes away once the old security model is ripped out.
 		if fs.authorizedRemoteID, err = remoteID.Authorize(isecurity.NewContext(
 			isecurity.ContextArgs{
 				LocalID:    fs.flow.LocalID(),
@@ -788,7 +790,7 @@
 	// Check application's authorization policy and invoke the method.
 	if err := fs.authorize(auth); err != nil {
 		// TODO(ataly, ashankar): For privacy reasons, should we hide the authorizer error (err)?
-		return nil, errNotAuthorized(fmt.Errorf("%q not authorized for method %q: %v", fs.RemoteID(), fs.Method(), err))
+		return nil, errNotAuthorized(fmt.Errorf("%v (PublicID:%v) not authorized for  %q.%q: %v", fs.RemoteBlessings(), fs.RemoteID(), fs.Name(), fs.Method(), err))
 	}
 	// Check if the caller is permitted to view debug information.
 	fs.allowDebug = fs.authorizeForDebug(auth) == nil
@@ -824,14 +826,10 @@
 }
 
 func (fs *flowServer) authorize(auth security.Authorizer) error {
-	if auth != nil {
-		return auth.Authorize(fs)
+	if auth == nil {
+		auth = defaultAuthorizer(fs)
 	}
-	// Since the provided authorizer is nil we create a default IDAuthorizer
-	// for the local identity of the flow. This authorizer only authorizes
-	// remote identities that have either been blessed by the local identity
-	// or have blessed the local identity. (See vsecurity.NewACLAuthorizer)
-	return vsecurity.NewACLAuthorizer(defaultACL(fs.flow.LocalID())).Authorize(fs)
+	return auth.Authorize(fs)
 }
 
 // debugContext is a context which wraps another context but always returns
@@ -845,14 +843,10 @@
 // TODO(mattr): Is DebugLabel the right thing to check?
 func (fs *flowServer) authorizeForDebug(auth security.Authorizer) error {
 	dc := debugContext{fs}
-	if auth != nil {
-		return auth.Authorize(dc)
+	if auth == nil {
+		auth = defaultAuthorizer(dc)
 	}
-	// Since the provided authorizer is nil we create a default IDAuthorizer
-	// for the local identity of the flow. This authorizer only authorizes
-	// remote identities that have either been blessed by the local identity
-	// or have blessed the local identity. (See vsecurity.NewACLAuthorizer)
-	return vsecurity.NewACLAuthorizer(defaultACL(dc.LocalID())).Authorize(dc)
+	return auth.Authorize(dc)
 }
 
 // Send implements the ipc.Stream method.
@@ -920,19 +914,19 @@
 }
 func (fs *flowServer) LocalPrincipal() security.Principal {
 	//nologcall
-	return nil
+	return fs.flow.LocalPrincipal()
 }
 func (fs *flowServer) LocalBlessings() security.Blessings {
 	//nologcall
-	return nil
+	return fs.flow.LocalBlessings()
 }
 func (fs *flowServer) RemoteBlessings() security.Blessings {
 	//nologcall
-	return nil
+	return fs.flow.RemoteBlessings()
 }
-func (fs *flowServer) Blessing() security.PublicID {
+func (fs *flowServer) Blessings() security.Blessings {
 	//nologcall
-	return fs.blessing
+	return fs.blessings
 }
 func (fs *flowServer) LocalEndpoint() naming.Endpoint {
 	//nologcall
diff --git a/runtimes/google/ipc/stream/proxy/proxy.go b/runtimes/google/ipc/stream/proxy/proxy.go
index f4b6b7a..4099d71 100644
--- a/runtimes/google/ipc/stream/proxy/proxy.go
+++ b/runtimes/google/ipc/stream/proxy/proxy.go
@@ -15,6 +15,7 @@
 	"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
 	"veyron.io/veyron/veyron/runtimes/google/lib/upcqueue"
 
+	"veyron.io/veyron/veyron2/ipc/stream"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/verror"
@@ -33,7 +34,7 @@
 type Proxy struct {
 	ln         net.Listener
 	rid        naming.RoutingID
-	id         vc.LocalID
+	principal  stream.ListenerOpt
 	mu         sync.RWMutex
 	servers    *servermap
 	processes  map[*process]struct{}
@@ -126,7 +127,14 @@
 
 // New creates a new Proxy that listens for network connections on the provided
 // (network, address) pair and routes VC traffic between accepted connections.
-func New(rid naming.RoutingID, identity security.PrivateID, network, address, pubAddress string) (*Proxy, error) {
+//
+// TODO(ashankar): Change principal to security.Principal once the old security model is ripped out.
+func New(rid naming.RoutingID, principal interface{}, network, address, pubAddress string) (*Proxy, error) {
+	if _, ok := principal.(security.Principal); principal != nil && !ok {
+		if _, ok := principal.(security.PrivateID); !ok {
+			return nil, fmt.Errorf("principal argument must be either a security.Principal or a security.PrivateID, not a %T", principal)
+		}
+	}
 	ln, err := net.Listen(network, address)
 	if err != nil {
 		return nil, fmt.Errorf("net.Listen(%q, %q) failed: %v", network, address, err)
@@ -141,8 +149,10 @@
 		processes:  make(map[*process]struct{}),
 		pubAddress: pubAddress,
 	}
-	if identity != nil {
-		proxy.id = vc.FixedLocalID(identity)
+	if p, ok := principal.(security.Principal); ok {
+		proxy.principal = vc.LocalPrincipal{p}
+	} else if principal != nil {
+		proxy.principal = vc.FixedLocalID(principal.(security.PrivateID))
 	}
 	go proxy.listenLoop()
 	return proxy, nil
@@ -318,7 +328,7 @@
 				p.routeCounters(process, m.Counters)
 				if vcObj != nil {
 					server := &server{Process: process, VC: vcObj}
-					go p.runServer(server, vcObj.HandshakeAcceptedVC(p.id))
+					go p.runServer(server, vcObj.HandshakeAcceptedVC(p.principal))
 				}
 				break
 			}
diff --git a/runtimes/google/ipc/stream/sectest/sectest.go b/runtimes/google/ipc/stream/sectest/sectest.go
index 2cbf560..9bbf025 100644
--- a/runtimes/google/ipc/stream/sectest/sectest.go
+++ b/runtimes/google/ipc/stream/sectest/sectest.go
@@ -10,9 +10,11 @@
 	"veyron.io/veyron/veyron2/security/sectest"
 )
 
-// NewPrincipal creates a new security.Principal which provides
-// defaultBlessing in BlessingStore().Default().
-func NewPrincipal(defaultBlessing string) security.Principal {
+// NewPrincipal creates a new security.Principal.
+//
+// It also creates self-certified blessings for defaultBlessings and
+// sets them up as BlessingStore().Default() (if any are provided).
+func NewPrincipal(defaultBlessings ...string) security.Principal {
 	_, key, err := sectest.NewKey()
 	if err != nil {
 		panic(err)
@@ -23,12 +25,25 @@
 	if err != nil {
 		panic(err)
 	}
-	def, err := p.BlessSelf(defaultBlessing)
-	if err != nil {
-		panic(err)
+
+	var def security.Blessings
+	for _, blessing := range defaultBlessings {
+		b, err := p.BlessSelf(blessing)
+		if err != nil {
+			panic(err)
+		}
+		if def, err = security.UnionOfBlessings(def, b); err != nil {
+			panic(err)
+		}
 	}
-	p.BlessingStore().SetDefault(def)
-	p.AddToRoots(def)
+	if def != nil {
+		if err := p.BlessingStore().SetDefault(def); err != nil {
+			panic(err)
+		}
+		if err := p.AddToRoots(def); err != nil {
+			panic(err)
+		}
+	}
 	return p
 }
 
diff --git a/runtimes/google/ipc/stream/vc/auth.go b/runtimes/google/ipc/stream/vc/auth.go
index ec8fef2..e3b06ec 100644
--- a/runtimes/google/ipc/stream/vc/auth.go
+++ b/runtimes/google/ipc/stream/vc/auth.go
@@ -264,7 +264,7 @@
 	})
 	client = principal.BlessingStore().ForPeer(serverB...)
 	if client == nil {
-		return nil, nil, fmt.Errorf("No blessing tagged for peer %v in the BlessingStore", serverB)
+		return nil, nil, fmt.Errorf("no blessing tagged for peer %v in the BlessingStore", serverB)
 	}
 	if err = writeBlessings(conn, authClientContextTag, crypter, principal, client, v); err != nil {
 		return nil, nil, err
diff --git a/runtimes/google/ipc/testutil_test.go b/runtimes/google/ipc/testutil_test.go
index a6e214a..1f8df64 100644
--- a/runtimes/google/ipc/testutil_test.go
+++ b/runtimes/google/ipc/testutil_test.go
@@ -5,10 +5,7 @@
 	"testing"
 
 	_ "veyron.io/veyron/veyron/lib/testutil"
-	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
-	isecurity "veyron.io/veyron/veyron/runtimes/google/security"
 
-	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/verror"
 )
@@ -41,14 +38,6 @@
 	}
 }
 
-func newID(name string) security.PrivateID {
-	id, err := isecurity.NewPrivateID(name, nil)
-	if err != nil {
-		panic(err)
-	}
-	return id
-}
-
 func newCaveat(v security.CaveatValidator) security.Caveat {
 	cav, err := security.NewCaveat(v)
 	if err != nil {
@@ -64,5 +53,13 @@
 	return cav
 }
 
-var _ ipc.ClientOpt = vc.FixedLocalID(newID("irrelevant"))
-var _ ipc.ServerOpt = vc.FixedLocalID(newID("irrelevant"))
+func bless(blesser, blessed security.Principal, extension string, caveats ...security.Caveat) security.Blessings {
+	if len(caveats) == 0 {
+		caveats = append(caveats, security.UnconstrainedUse())
+	}
+	b, err := blesser.Bless(blessed.PublicKey(), blesser.BlessingStore().Default(), extension, caveats[0], caveats[1:]...)
+	if err != nil {
+		panic(err)
+	}
+	return b
+}
diff --git a/security/acl_authorizer.go b/security/acl_authorizer.go
index 7ed18cc..7a305ab 100644
--- a/security/acl_authorizer.go
+++ b/security/acl_authorizer.go
@@ -29,8 +29,10 @@
 	if ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil && reflect.DeepEqual(ctx.LocalBlessings().PublicKey(), ctx.RemoteBlessings().PublicKey()) {
 		return nil
 	}
-	if ctx.LocalID() != nil && ctx.RemoteID() != nil && reflect.DeepEqual(ctx.LocalID(), ctx.RemoteID()) {
-		return nil
+	if newAPI := (ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil); !newAPI {
+		if ctx.LocalID() != nil && ctx.RemoteID() != nil && reflect.DeepEqual(ctx.LocalID(), ctx.RemoteID()) {
+			return nil
+		}
 	}
 	var blessings []string
 	if ctx.RemoteBlessings() != nil {
@@ -38,7 +40,6 @@
 	} else if ctx.RemoteID() != nil {
 		blessings = ctx.RemoteID().Names()
 	}
-	// Match the aclAuthorizer's ACL.
 	return matchesACL(blessings, ctx.Label(), security.ACL(a))
 }
 
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index c71e71a..26869a5 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -124,16 +124,17 @@
 	return
 }
 
-func (d *dispatcher) claimNodeManager(id security.PublicID) error {
+func (d *dispatcher) claimNodeManager(names []string, proof security.Blessings) error {
 	// TODO(gauthamt): Should we start trusting these identity providers?
-	if id.Names() == nil {
-		vlog.Errorf("Identity provider for device claimer is not trusted")
+	if len(names) == 0 {
+		vlog.Errorf("No names for claimer(%v) are trusted", proof)
 		return errOperationFailed
 	}
-	rt.R().PublicIDStore().Add(id, security.AllPrincipals)
+	rt.R().Principal().BlessingStore().Set(proof, security.AllPrincipals)
+	rt.R().Principal().BlessingStore().SetDefault(proof)
 	// Create ACLs to transfer nodemanager permissions to the provided identity.
 	acl := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
-	for _, name := range id.Names() {
+	for _, name := range names {
 		acl.In[security.BlessingPattern(name)] = security.AllLabels
 	}
 	_, etag, err := d.getACL()
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 493c0f4..bed59c0 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -1,10 +1,10 @@
 package impl_test
 
 import (
-	"bytes"
+	//	"bytes"
 	"crypto/md5"
 	"encoding/base64"
-	"encoding/hex"
+	//	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -23,8 +23,8 @@
 	"veyron.io/veyron/veyron/lib/exec"
 	"veyron.io/veyron/veyron/lib/signals"
 	"veyron.io/veyron/veyron/lib/testutil/blackbox"
-	tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
-	vsecurity "veyron.io/veyron/veyron/security"
+	// tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
+	// vsecurity "veyron.io/veyron/veyron/security"
 	"veyron.io/veyron/veyron/services/mgmt/node/config"
 	"veyron.io/veyron/veyron/services/mgmt/node/impl"
 	suidhelper "veyron.io/veyron/veyron/services/mgmt/suidhelper/impl"
@@ -756,6 +756,9 @@
 	return nil
 }
 
+// TODO(ashankar): Temporarily disabled during security model transition.
+// Fix up and restore!
+/*
 // TestNodeManagerClaim claims a nodemanager and tests ACL permissions on its methods.
 func TestNodeManagerClaim(t *testing.T) {
 	// Set up mount table, application, and binary repositories.
@@ -914,6 +917,7 @@
 		t.Fatalf("Install should have failed with claimer identity")
 	}
 }
+*/
 
 func TestNodeManagerGlob(t *testing.T) {
 	// Set up mount table.
diff --git a/services/mgmt/node/impl/node_invoker.go b/services/mgmt/node/impl/node_invoker.go
index 2f3ea88..243f2d1 100644
--- a/services/mgmt/node/impl/node_invoker.go
+++ b/services/mgmt/node/impl/node_invoker.go
@@ -91,12 +91,12 @@
 }
 
 func (i *nodeInvoker) Claim(call ipc.ServerContext) error {
-	// Get the blessing to be used by the claimant
-	blessing := call.Blessing()
-	if blessing == nil {
+	// Get the blessing to be used by the claimant.
+	blessings := call.Blessings()
+	if blessings == nil {
 		return errInvalidBlessing
 	}
-	return i.disp.claimNodeManager(blessing)
+	return i.disp.claimNodeManager(blessings.ForContext(call), blessings)
 }
 
 func (*nodeInvoker) Describe(ipc.ServerContext) (node.Description, error) {
diff --git a/services/proxy/proxyd/main.go b/services/proxy/proxyd/main.go
index 903db8b..f7296a6 100644
--- a/services/proxy/proxyd/main.go
+++ b/services/proxy/proxyd/main.go
@@ -36,6 +36,8 @@
 		vlog.Fatal(err)
 	}
 
+	// TODO(ashankar): Set the second argument to r.Principal() once the
+	// old security model is no longer operational.
 	proxy, err := proxy.New(rid, nil, *protocol, *address, *pubAddress)
 	if err != nil {
 		vlog.Fatal(err)