security: UUID-based caveat validator registration (Step 1/3).

The current Caveat scheme uses the package path of the CaveatValidator
implementation to associate a caveat with its validation function.
Relying on such type information is brittle because any renaming breaks
caveat validation.

Instead, we (toddw@, bprosnitz@, ataly@ and ashankar@) decided to use
explicit unique ids to associate caveats with their validation
functions.

This commit implements the registration mechanism for associating caveat
validation functions with caveats.

As a bonus, this scheme also improves caveat validation performance
by a factor of 3x, as the VOM-decoding happens into concrete
types (instead of into an interface). Benchmarks added in this CL
on my laptop show:
BenchmarkValidateCaveat	  200000	     13337 ns/op
while a comparable benchmark on the older style caveat parsing and
validation showed ~45K ns/op.

This is the first (& biggest) step of a 3 step process to be
completed in the next week:
(1) This change: Introducing a registry that associates
    caveats identified by UUIDs with validation functions.
(2) Once all binaries have been rebuilt & restarted with this
    change, stop populating the ValidatorVOM field and
    rebuild & restart services again
(3) Once all blessings have been refreshed (so that the
    ValidatorVOM field is not populated), drop the field
    altogether.

MultiPart: 1/2

Change-Id: I590414110d71cb4c70d9069d01161df15b7ebd49
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 3e85647..da0d385 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -69,16 +69,6 @@
 	c.Unlock()
 }
 
-type fakeTimeCaveat int
-
-func (c fakeTimeCaveat) Validate(security.Context) error {
-	now := clock.Now()
-	if now > int(c) {
-		return fmt.Errorf("fakeTimeCaveat expired: now=%d > then=%d", now, c)
-	}
-	return nil
-}
-
 // We need a special way to create contexts for tests.  We
 // can't create a real runtime in the runtime implementation
 // so we use a fake one that panics if used.  The runtime
@@ -195,15 +185,26 @@
 type dischargeServer struct{}
 
 func (*dischargeServer) Discharge(ctx ipc.ServerCall, cav vdl.AnyRep, _ security.DischargeImpetus) (vdl.AnyRep, error) {
-	c, ok := cav.(security.ThirdPartyCaveat)
-	if !ok {
-		return nil, fmt.Errorf("discharger: unknown caveat(%T)", cav)
+	// TODO(ashankar): During refactoring, this "if" statement must remain.
+	// After security.Caveat.ValidatorVOM is removed, the "else" part can go away.
+	var tpc security.ThirdPartyCaveat
+	if c, ok := cav.(security.Caveat); ok {
+		tpc = c.ThirdPartyDetails()
+	} else {
+		tpc, _ = cav.(security.ThirdPartyCaveat)
 	}
-	if err := c.Dischargeable(ctx); err != nil {
-		return nil, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", c, err)
+	if tpc == nil {
+		return nil, fmt.Errorf("discharger: %T does not represent a third-party caveat", cav)
+	}
+	if err := tpc.Dischargeable(ctx); err != nil {
+		return nil, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", cav, err)
 	}
 	// Add a fakeTimeCaveat to be able to control discharge expiration via 'clock'.
-	return ctx.LocalPrincipal().MintDischarge(c, newCaveat(fakeTimeCaveat(clock.Now())))
+	expiry, err := security.NewCaveat(fakeTimeCaveat, clock.Now())
+	if err != nil {
+		return nil, fmt.Errorf("failed to create an expiration on the discharge: %v", err)
+	}
+	return ctx.LocalPrincipal().MintDischarge(cav, expiry)
 }
 
 func startServer(t *testing.T, principal security.Principal, sm stream.Manager, ns naming.Namespace, name string, disp ipc.Dispatcher, opts ...ipc.ServerOpt) (naming.Endpoint, ipc.Server) {
@@ -751,7 +752,7 @@
 	if err != nil {
 		panic(err)
 	}
-	return newCaveat(tpc)
+	return tpc
 }
 
 // dischargeTestServer implements the discharge service. Always fails to
@@ -784,14 +785,10 @@
 
 		mkClient = func(req security.ThirdPartyRequirements) vc.LocalPrincipal {
 			// Setup the client so that it shares a blessing with a third-party caveat with the server.
-			tpc, err := security.NewPublicKeyCaveat(pdischarger.PublicKey(), "mountpoint/discharger", req, security.UnconstrainedUse())
+			cav, err := security.NewPublicKeyCaveat(pdischarger.PublicKey(), "mountpoint/discharger", req, security.UnconstrainedUse())
 			if err != nil {
 				t.Fatalf("Failed to create ThirdPartyCaveat(%+v): %v", req, err)
 			}
-			cav, err := security.NewCaveat(tpc)
-			if err != nil {
-				t.Fatal(err)
-			}
 			b, err := pclient.BlessSelf("client_for_server", cav)
 			if err != nil {
 				t.Fatalf("BlessSelf failed: %v", err)
@@ -1632,7 +1629,7 @@
 	if err != nil {
 		t.Error(err)
 	}
-	dc.PrepareDischarges(testContext(), []security.ThirdPartyCaveat{tpcav2}, security.DischargeImpetus{})
+	dc.PrepareDischarges(testContext(), []security.ThirdPartyCaveat{tpcav2.ThirdPartyDetails()}, security.DischargeImpetus{})
 
 	// Ensure that discharger1 was not called and discharger2 was called.
 	if discharger1.called {
@@ -1745,11 +1742,18 @@
 	clientB.Close()
 }
 
-func init() {
-	vdl.Register(fakeTimeCaveat(0))
+var fakeTimeCaveat = security.CaveatDescriptor{
+	Id:        uniqueid.Id{0x18, 0xba, 0x6f, 0x84, 0xd5, 0xec, 0xdb, 0x9b, 0xf2, 0x32, 0x19, 0x5b, 0x53, 0x92, 0x80, 0x0},
+	ParamType: vdl.TypeOf(int64(0)),
 }
 
 func TestMain(m *testing.M) {
 	testutil.Init()
+	security.RegisterCaveatValidator(fakeTimeCaveat, func(_ security.Context, t int64) error {
+		if now := clock.Now(); now > int(t) {
+			return fmt.Errorf("fakeTimeCaveat expired: now=%d > then=%d", now, t)
+		}
+		return nil
+	})
 	os.Exit(m.Run())
 }
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index 05f4edd..81c1ece 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -180,13 +180,6 @@
 var _ vc.DischargeClient = (mockDischargeClient)(nil)
 
 func TestHandshakeWithDischargesTLS(t *testing.T) {
-	newCaveat := func(validator security.CaveatValidator) security.Caveat {
-		cav, err := security.NewCaveat(validator)
-		if err != nil {
-			t.Fatal(err)
-		}
-		return cav
-	}
 	var (
 		discharger = tsecurity.NewPrincipal("discharger")
 		client     = tsecurity.NewPrincipal()
@@ -204,10 +197,10 @@
 
 	// Setup 'client' and 'server' so that they use a blessing from 'root' with a third-party caveat
 	// during VC handshake.
-	if err := root.Bless(client, "client", newCaveat(tpcav)); err != nil {
+	if err := root.Bless(client, "client", tpcav); err != nil {
 		t.Fatal(err)
 	}
-	if err := root.Bless(server, "server", newCaveat(tpcav)); err != nil {
+	if err := root.Bless(server, "server", tpcav); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/runtimes/google/ipc/testutil_test.go b/runtimes/google/ipc/testutil_test.go
index 01f9444..7eb79eb 100644
--- a/runtimes/google/ipc/testutil_test.go
+++ b/runtimes/google/ipc/testutil_test.go
@@ -50,14 +50,6 @@
 	}
 }
 
-func newCaveat(v security.CaveatValidator) security.Caveat {
-	cav, err := security.NewCaveat(v)
-	if err != nil {
-		panic(err)
-	}
-	return cav
-}
-
 func mkCaveat(cav security.Caveat, err error) security.Caveat {
 	if err != nil {
 		panic(err)
diff --git a/runtimes/google/rt/ipc_test.go b/runtimes/google/rt/ipc_test.go
index 3d72538..95a95c1 100644
--- a/runtimes/google/rt/ipc_test.go
+++ b/runtimes/google/rt/ipc_test.go
@@ -59,14 +59,6 @@
 	return ret
 }
 
-func newCaveat(v security.CaveatValidator) security.Caveat {
-	cav, err := security.NewCaveat(v)
-	if err != nil {
-		panic(err)
-	}
-	return cav
-}
-
 func mkCaveat(cav security.Caveat, err error) security.Caveat {
 	if err != nil {
 		panic(err)
@@ -89,7 +81,7 @@
 	if err != nil {
 		panic(err)
 	}
-	return newCaveat(tpc)
+	return tpc
 }
 
 func startServer(ctx *context.T, s interface{}) (ipc.Server, string, error) {
diff --git a/security/agent/agent_test.go b/security/agent/agent_test.go
index a6593f6..0c9005e 100644
--- a/security/agent/agent_test.go
+++ b/security/agent/agent_test.go
@@ -167,7 +167,7 @@
 	return
 }
 
-func (p *mockPrincipal) MintDischarge(security.ThirdPartyCaveat, security.Caveat, ...security.Caveat) (security.Discharge, error) {
+func (p *mockPrincipal) MintDischarge(interface{}, security.Caveat, ...security.Caveat) (security.Discharge, error) {
 	defer p.reset()
 	d, _ := p.NextResult.(security.Discharge)
 	return d, p.NextError
@@ -311,7 +311,7 @@
 	return b
 }
 
-func newThirdPartyCaveatAndDischarge(t *testing.T) (security.ThirdPartyCaveat, security.Discharge) {
+func newThirdPartyCaveatAndDischarge(t *testing.T) (security.Caveat, security.Discharge) {
 	p := newPrincipal(t)
 	c, err := security.NewPublicKeyCaveat(p.PublicKey(), "location", security.ThirdPartyRequirements{}, newCaveat(security.MethodCaveat("method")))
 	if err != nil {
diff --git a/security/agent/client.go b/security/agent/client.go
index 77c6dde..99854c5 100644
--- a/security/agent/client.go
+++ b/security/agent/client.go
@@ -120,10 +120,9 @@
 	return
 }
 
-func (c *client) MintDischarge(tp security.ThirdPartyCaveat, caveat security.Caveat, additionalCaveats ...security.Caveat) (security.Discharge, error) {
+func (c *client) MintDischarge(tp interface{}, caveat security.Caveat, additionalCaveats ...security.Caveat) (security.Discharge, error) {
 	var discharge security.Discharge
-	err := c.caller.call("MintDischarge", results(&discharge), vdl.AnyRep(tp), caveat, additionalCaveats)
-	if err != nil {
+	if err := c.caller.call("MintDischarge", results(&discharge), vdl.AnyRep(tp), caveat, additionalCaveats); err != nil {
 		return nil, err
 	}
 	return discharge, nil
diff --git a/security/agent/server/server.go b/security/agent/server/server.go
index 7175a57..a274c53 100644
--- a/security/agent/server/server.go
+++ b/security/agent/server/server.go
@@ -7,7 +7,6 @@
 	"crypto/sha512"
 	"crypto/x509"
 	"encoding/base64"
-	"fmt"
 	"io"
 	"net"
 	"os"
@@ -254,11 +253,7 @@
 }
 
 func (a agentd) MintDischarge(_ ipc.ServerContext, tp vdl.AnyRep, caveat security.Caveat, additionalCaveats []security.Caveat) (vdl.AnyRep, error) {
-	tpCaveat, ok := tp.(security.ThirdPartyCaveat)
-	if !ok {
-		return nil, fmt.Errorf("provided caveat of type %T does not implement security.ThirdPartyCaveat", tp)
-	}
-	return a.principal.MintDischarge(tpCaveat, caveat, additionalCaveats...)
+	return a.principal.MintDischarge(tp, caveat, additionalCaveats...)
 }
 
 func (a keymgr) newKey(in_memory bool) (id []byte, p security.Principal, err error) {
diff --git a/security/audit/principal.go b/security/audit/principal.go
index b7f9347..879d8f6 100644
--- a/security/audit/principal.go
+++ b/security/audit/principal.go
@@ -46,7 +46,7 @@
 	return sig, nil
 }
 
-func (p *auditingPrincipal) MintDischarge(tp security.ThirdPartyCaveat, caveat security.Caveat, additionalCaveats ...security.Caveat) (security.Discharge, error) {
+func (p *auditingPrincipal) MintDischarge(tp interface{}, caveat security.Caveat, additionalCaveats ...security.Caveat) (security.Discharge, error) {
 	d, err := p.principal.MintDischarge(tp, caveat, additionalCaveats...)
 	// No need to log the discharge
 	if err = p.audit(err, "MintDischarge", addCaveats(args{tp, caveat}, additionalCaveats...), nil); err != nil {
diff --git a/security/audit/principal_test.go b/security/audit/principal_test.go
index 4b0d977..f080af7 100644
--- a/security/audit/principal_test.go
+++ b/security/audit/principal_test.go
@@ -156,7 +156,7 @@
 	return
 }
 
-func (p *mockPrincipal) MintDischarge(security.ThirdPartyCaveat, security.Caveat, ...security.Caveat) (security.Discharge, error) {
+func (p *mockPrincipal) MintDischarge(interface{}, security.Caveat, ...security.Caveat) (security.Discharge, error) {
 	defer p.reset()
 	d, _ := p.NextResult.(security.Discharge)
 	return d, p.NextError
@@ -249,7 +249,7 @@
 	return b
 }
 
-func newThirdPartyCaveatAndDischarge(t *testing.T) (security.ThirdPartyCaveat, security.Discharge) {
+func newThirdPartyCaveatAndDischarge(t *testing.T) (security.Caveat, security.Discharge) {
 	p := newPrincipal(t)
 	c, err := security.NewPublicKeyCaveat(p.PublicKey(), "location", security.ThirdPartyRequirements{}, newCaveat(security.MethodCaveat("method")))
 	if err != nil {
diff --git a/services/identity/auditor/blessing_auditor_test.go b/services/identity/auditor/blessing_auditor_test.go
index 5b1f50b..ab0dadc 100644
--- a/services/identity/auditor/blessing_auditor_test.go
+++ b/services/identity/auditor/blessing_auditor_test.go
@@ -43,8 +43,8 @@
 		{
 			Extension:          "special/guests/foo@bar.com/caveatAndRevocation",
 			Email:              "foo@bar.com",
-			Caveats:            []security.Caveat{expiryCaveat, newCaveat(security.NewCaveat(revocationCaveat))},
-			RevocationCaveatID: revocationCaveat.ID(),
+			Caveats:            []security.Caveat{expiryCaveat, revocationCaveat},
+			RevocationCaveatID: revocationCaveat.ThirdPartyDetails().ID(),
 			Blessings:          newBlessing(t, p, "test/foo@bar.com/caveatAndRevocation"),
 		},
 	}
@@ -86,7 +86,7 @@
 	}
 }
 
-func newThirdPartyCaveat(t *testing.T, p security.Principal) security.ThirdPartyCaveat {
+func newThirdPartyCaveat(t *testing.T, p security.Principal) security.Caveat {
 	tp, err := security.NewPublicKeyCaveat(p.PublicKey(), "location", security.ThirdPartyRequirements{}, newCaveat(security.MethodCaveat("method")))
 	if err != nil {
 		t.Fatal(err)
diff --git a/services/identity/revocation/caveat.vdl b/services/identity/revocation/caveat.vdl
new file mode 100644
index 0000000..673e29f
--- /dev/null
+++ b/services/identity/revocation/caveat.vdl
@@ -0,0 +1,17 @@
+package revocation
+
+import (
+	"v.io/core/veyron2/uniqueid"
+	"v.io/core/veyron2/security"
+)
+
+// NotRevokedCaveat is used to implement revocation.
+// It validates iff the parameter is not included in a list of blacklisted
+// values.
+//
+// The third-party discharging service checks this revocation caveat against a
+// database of blacklisted (revoked) keys before issuing a discharge.
+const NotRevokedCaveat = security.CaveatDescriptor{
+    Id:        uniqueid.Id{0x4b, 0x46, 0x5c, 0x56, 0x37, 0x79, 0xd1, 0x3b, 0x7b, 0xa3, 0xa7, 0xd6, 0xa5, 0x34, 0x80, 0x0},
+    ParamType: typeobject([]byte),
+}
diff --git a/services/identity/revocation/caveat.vdl.go b/services/identity/revocation/caveat.vdl.go
new file mode 100644
index 0000000..2990f95
--- /dev/null
+++ b/services/identity/revocation/caveat.vdl.go
@@ -0,0 +1,41 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: caveat.vdl
+
+package revocation
+
+import (
+	"v.io/core/veyron2/security"
+
+	"v.io/core/veyron2/uniqueid"
+
+	// The non-user imports are prefixed with "__" to prevent collisions.
+	__vdl "v.io/core/veyron2/vdl"
+)
+
+// NotRevokedCaveat is used to implement revocation.
+// It validates iff the parameter is not included in a list of blacklisted
+// values.
+//
+// The third-party discharging service checks this revocation caveat against a
+// database of blacklisted (revoked) keys before issuing a discharge.
+var NotRevokedCaveat = security.CaveatDescriptor{
+	Id: uniqueid.Id{
+		75,
+		70,
+		92,
+		86,
+		55,
+		121,
+		209,
+		59,
+		123,
+		163,
+		167,
+		214,
+		165,
+		52,
+		128,
+		0,
+	},
+	ParamType: __vdl.TypeOf([]byte("")),
+}
diff --git a/services/identity/revocation/revocation_manager.go b/services/identity/revocation/revocation_manager.go
index 235b88f..ed685ff 100644
--- a/services/identity/revocation/revocation_manager.go
+++ b/services/identity/revocation/revocation_manager.go
@@ -10,6 +10,7 @@
 
 	"v.io/core/veyron2/security"
 	"v.io/core/veyron2/vdl"
+	"v.io/core/veyron2/vom"
 )
 
 // RevocationManager persists information for revocation caveats to provided discharges and allow for future revocations.
@@ -50,18 +51,22 @@
 	if _, err := rand.Read(revocation[:]); err != nil {
 		return empty, err
 	}
-	restriction, err := security.NewCaveat(revocationCaveat(revocation))
+	restriction, err := security.NewCaveat(NotRevokedCaveat, revocation[:])
 	if err != nil {
 		return empty, err
 	}
+	// TODO(ashankar): Remove when removing ValidatorVOM
+	if restriction.ValidatorVOM, err = vom.Encode(revocationCaveat(revocation)); err != nil {
+		return empty, err
+	}
 	cav, err := security.NewPublicKeyCaveat(discharger, dischargerLocation, security.ThirdPartyRequirements{}, restriction)
 	if err != nil {
 		return empty, err
 	}
-	if err = revocationDB.InsertCaveat(cav.ID(), revocation[:]); err != nil {
+	if err = revocationDB.InsertCaveat(cav.ThirdPartyDetails().ID(), revocation[:]); err != nil {
 		return empty, err
 	}
-	return security.NewCaveat(cav)
+	return cav, nil
 }
 
 // Revoke disables discharges from being issued for the provided third-party caveat.
@@ -79,22 +84,29 @@
 	return timestamp
 }
 
-type revocationCaveat [16]byte
-
-func (cav revocationCaveat) Validate(security.Context) error {
+func isRevoked(_ security.Context, key []byte) error {
 	revocationLock.RLock()
 	if revocationDB == nil {
 		revocationLock.RUnlock()
 		return fmt.Errorf("missing call to NewRevocationManager")
 	}
 	revocationLock.RUnlock()
-	revoked, err := revocationDB.IsRevoked(cav[:])
+	revoked, err := revocationDB.IsRevoked(key)
 	if revoked {
 		return fmt.Errorf("revoked")
 	}
 	return err
 }
 
+// TODO(ashankar): Remove when removing security.Caveat.ValidatorVOM.
+type revocationCaveat [16]byte
+
+func (cav revocationCaveat) Validate(ctx security.Context) error {
+	return isRevoked(ctx, cav[:])
+}
+
 func init() {
+	// TODO(ashankar): Remove when removing security.Caveat.ValidatorVOM
 	vdl.Register(revocationCaveat{})
+	security.RegisterCaveatValidator(NotRevokedCaveat, isRevoked)
 }
diff --git a/services/security/discharger.vdl b/services/security/discharger.vdl
index d43ed74..c1747cc 100644
--- a/services/security/discharger.vdl
+++ b/services/security/discharger.vdl
@@ -10,7 +10,7 @@
   //
   // Caveat and Discharge are of type ThirdPartyCaveat and Discharge
   // respectively. (not enforced here because vdl does not know these types)
-  // TODO(ataly,ashankar): Figure out a VDL representation for ThirdPartyCaveat
-  // and Discharge and use those here?
+  // TODO(ataly,ashankar): The type of Caveat should become security.Caveat and
+  // we have to figure out an alternative to any for the return Discharge.
   Discharge(Caveat any, Impetus security.DischargeImpetus) (Discharge any | error)
 }
diff --git a/services/security/discharger.vdl.go b/services/security/discharger.vdl.go
index 771c5f1..f086448 100644
--- a/services/security/discharger.vdl.go
+++ b/services/security/discharger.vdl.go
@@ -24,8 +24,8 @@
 	//
 	// Caveat and Discharge are of type ThirdPartyCaveat and Discharge
 	// respectively. (not enforced here because vdl does not know these types)
-	// TODO(ataly,ashankar): Figure out a VDL representation for ThirdPartyCaveat
-	// and Discharge and use those here?
+	// TODO(ataly,ashankar): The type of Caveat should become security.Caveat and
+	// we have to figure out an alternative to any for the return Discharge.
 	Discharge(ctx *__context.T, Caveat __vdl.AnyRep, Impetus security.DischargeImpetus, opts ...__ipc.CallOpt) (Discharge __vdl.AnyRep, err error)
 }
 
@@ -80,8 +80,8 @@
 	//
 	// Caveat and Discharge are of type ThirdPartyCaveat and Discharge
 	// respectively. (not enforced here because vdl does not know these types)
-	// TODO(ataly,ashankar): Figure out a VDL representation for ThirdPartyCaveat
-	// and Discharge and use those here?
+	// TODO(ataly,ashankar): The type of Caveat should become security.Caveat and
+	// we have to figure out an alternative to any for the return Discharge.
 	Discharge(ctx __ipc.ServerContext, Caveat __vdl.AnyRep, Impetus security.DischargeImpetus) (Discharge __vdl.AnyRep, err error)
 }
 
@@ -143,7 +143,7 @@
 	Methods: []__ipc.MethodDesc{
 		{
 			Name: "Discharge",
-			Doc:  "// Discharge is called by a principal that holds a blessing with a third\n// party caveat and seeks to get a discharge that proves the fulfillment of\n// this caveat.\n//\n// Caveat and Discharge are of type ThirdPartyCaveat and Discharge\n// respectively. (not enforced here because vdl does not know these types)\n// TODO(ataly,ashankar): Figure out a VDL representation for ThirdPartyCaveat\n// and Discharge and use those here?",
+			Doc:  "// Discharge is called by a principal that holds a blessing with a third\n// party caveat and seeks to get a discharge that proves the fulfillment of\n// this caveat.\n//\n// Caveat and Discharge are of type ThirdPartyCaveat and Discharge\n// respectively. (not enforced here because vdl does not know these types)\n// TODO(ataly,ashankar): The type of Caveat should become security.Caveat and\n// we have to figure out an alternative to any for the return Discharge.",
 			InArgs: []__ipc.ArgDesc{
 				{"Caveat", ``},  // __vdl.AnyRep
 				{"Impetus", ``}, // security.DischargeImpetus