ref/cmd/principal: Some principal getter additions.

As per https://github.com/veyron/release-issues/issues/991.

This CL adds:
(1): 'principal get recognizedroots' prints the recognized public keys and
     patterns.
(2): 'principal get peermap' prints the blessings to present to peers.
(3): --name and --rootkey flags to the 'get forpeer' and
     'get default' commands that prints either the name or the rootkey
     of the blessing.

Change-Id: Ib56161f66a2a5e7e736b0234f25a84555602bc31
diff --git a/cmd/principal/doc.go b/cmd/principal/doc.go
index 0e03614..40aea3c 100644
--- a/cmd/principal/doc.go
+++ b/cmd/principal/doc.go
@@ -208,7 +208,7 @@
  -remote-arg-file=
    If non-empty, the remote key, remote token, and principal will be written to
    the specified file in a JSON object. This can be provided to 'principal bless
-   --remote-arg-file FILE EXTENSION'.
+   --remote-arg-file FILE EXTENSION'
  -set-default=true
    If true, the blessings received will be set as the default blessing in the
    store
@@ -299,7 +299,7 @@
  -remote-arg-file=
    File containing bless arguments written by 'principal recvblessings
    -remote-arg-file FILE EXTENSION' command. This can be provided to bless in
-   place of --remote-key, --remote-token, and <principal>.
+   place of --remote-key, --remote-token, and <principal>
  -remote-key=
    Public key of the remote principal to bless (obtained from the
    'recvblessings' command run by the remote principal
@@ -383,8 +383,11 @@
    principal get <command>
 
 The principal get commands are:
-   default     Return blessings marked as default
-   forpeer     Return blessings marked for the provided peer
+   default         Return blessings marked as default
+   forpeer         Return blessings marked for the provided peer
+   recognizedroots Return recognized blessings, and their associated public key.
+   peermap         Shows the map from peer pattern to which blessing name to
+                   present.
 
 Principal Get Default
 
@@ -392,7 +395,14 @@
 the environment that this tool is running in.
 
 Usage:
-   principal get default
+   principal get default [flags]
+
+The principal get default flags are:
+ -name=false
+   If true, shows the value of the blessing name to be presented to the peer
+ -rootkey=false
+   If true, shows the value of the root key of the certificate chain to be
+   presented to the peer
 
 Principal Get Forpeer
 
@@ -400,7 +410,7 @@
 specified by the environment that this tool is running in.
 
 Usage:
-   principal get forpeer [<peer_1> ... <peer_k>]
+   principal get forpeer [flags] [<peer_1> ... <peer_k>]
 
 <peer_1> ... <peer_k> are the (human-readable string) blessings bound to the
 peer. The returned blessings are marked with a pattern that is matched by at
@@ -408,6 +418,33 @@
 blessings that are marked for all peers (i.e., blessings set on the store with
 the "..." pattern).
 
+The principal get forpeer flags are:
+ -name=false
+   If true, shows the value of the blessing name to be presented to the peer
+ -rootkey=false
+   If true, shows the value of the root key of the certificate chain to be
+   presented to the peer
+
+Principal Get Recognizedroots
+
+Shows list of blessing names that the principal recognizes, and their associated
+public key. If the principal is operating as a client, contacted servers must
+appear on this list. If the principal is operating as a server, clients must
+present blessings derived from this list.
+
+Usage:
+   principal get recognizedroots
+
+Principal Get Peermap
+
+Shows the map from peer pattern to which blessing name to present. If the
+principal operates as a server, it presents its default blessing to all peers.
+If the principal operates as a client, it presents the map value associated with
+the peer it contacts.
+
+Usage:
+   principal get peermap
+
 Principal Addtoroots
 
 Adds an identity provider to the set of recognized roots public keys for this
diff --git a/cmd/principal/main.go b/cmd/principal/main.go
index 41845d9..d706292 100644
--- a/cmd/principal/main.go
+++ b/cmd/principal/main.go
@@ -65,6 +65,10 @@
 	flagRecvBlessingsSetDefault bool
 	flagRecvBlessingsForPeer    string
 
+	// Flags for the commands the get blessings
+	flagBlessingsName    bool
+	flagBlessingsRootKey bool
+
 	errNoCaveats = fmt.Errorf("no caveats provided: it is generally dangerous to bless another principal without any caveats as that gives them almost unrestricted access to the blesser's credentials. If you really want to do this, set --require-caveats=false")
 	cmdDump      = &cmdline.Command{
 		Name:  "dump",
@@ -275,6 +279,40 @@
 		},
 	}
 
+	cmdGetTrustedRoots = &cmdline.Command{
+		Name:  "recognizedroots",
+		Short: "Return recognized blessings, and their associated public key.",
+		Long: `
+Shows list of blessing names that the principal recognizes, and their associated
+public key. If the principal is operating as a client, contacted servers must
+appear on this list. If the principal is operating as a server, clients must
+present blessings derived from this list.
+`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			ctx, shutdown := v23.Init()
+			defer shutdown()
+			fmt.Printf(v23.GetPrincipal(ctx).Roots().DebugString())
+			return nil
+		},
+	}
+
+	cmdGetPeerMap = &cmdline.Command{
+		Name:  "peermap",
+		Short: "Shows the map from peer pattern to which blessing name to present.",
+		Long: `
+Shows the map from peer pattern to which blessing name to present.
+If the principal operates as a server, it presents its default blessing to all peers.
+If the principal operates as a client, it presents the map value associated with
+the peer it contacts.
+`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			ctx, shutdown := v23.Init()
+			defer shutdown()
+			fmt.Printf(v23.GetPrincipal(ctx).BlessingStore().DebugString())
+			return nil
+		},
+	}
+
 	cmdGetForPeer = &cmdline.Command{
 		Name:  "forpeer",
 		Short: "Return blessings marked for the provided peer",
@@ -294,8 +332,7 @@
 		Run: func(cmd *cmdline.Command, args []string) error {
 			ctx, shutdown := v23.Init()
 			defer shutdown()
-			principal := v23.GetPrincipal(ctx)
-			return dumpBlessings(principal.BlessingStore().ForPeer(args...))
+			return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().ForPeer(args...))
 		},
 	}
 
@@ -309,8 +346,7 @@
 		Run: func(cmd *cmdline.Command, args []string) error {
 			ctx, shutdown := v23.Init()
 			defer shutdown()
-			principal := v23.GetPrincipal(ctx)
-			return dumpBlessings(principal.BlessingStore().Default())
+			return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().Default())
 		},
 	}
 
@@ -815,7 +851,7 @@
 	cmdBless.Flags.StringVar(&flagBlessWith, "with", "", "Path to file containing blessing to extend")
 	cmdBless.Flags.StringVar(&flagBlessRemoteKey, "remote-key", "", "Public key of the remote principal to bless (obtained from the 'recvblessings' command run by the remote principal")
 	cmdBless.Flags.StringVar(&flagBlessRemoteToken, "remote-token", "", "Token provided by principal running the 'recvblessings' command")
-	cmdBless.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "File containing bless arguments written by 'principal recvblessings -remote-arg-file FILE EXTENSION' command. This can be provided to bless in place of --remote-key, --remote-token, and <principal>.")
+	cmdBless.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "File containing bless arguments written by 'principal recvblessings -remote-arg-file FILE EXTENSION' command. This can be provided to bless in place of --remote-key, --remote-token, and <principal>")
 
 	cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsFrom, "from", "https://dev.v.io/auth/google", "URL to use to begin the seek blessings process")
 	cmdSeekBlessings.Flags.BoolVar(&flagSeekBlessingsSetDefault, "set-default", true, "If true, the blessings obtained will be set as the default blessing in the store")
@@ -831,7 +867,13 @@
 
 	cmdRecvBlessings.Flags.BoolVar(&flagRecvBlessingsSetDefault, "set-default", true, "If true, the blessings received will be set as the default blessing in the store")
 	cmdRecvBlessings.Flags.StringVar(&flagRecvBlessingsForPeer, "for-peer", string(security.AllPrincipals), "If non-empty, the blessings received will be marked for peers matching this pattern in the store")
-	cmdRecvBlessings.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "If non-empty, the remote key, remote token, and principal will be written to the specified file in a JSON object. This can be provided to 'principal bless --remote-arg-file FILE EXTENSION'.")
+	cmdRecvBlessings.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "If non-empty, the remote key, remote token, and principal will be written to the specified file in a JSON object. This can be provided to 'principal bless --remote-arg-file FILE EXTENSION'")
+
+	cmdGetForPeer.Flags.BoolVar(&flagBlessingsName, "name", false, "If true, shows the value of the blessing name to be presented to the peer")
+	cmdGetForPeer.Flags.BoolVar(&flagBlessingsRootKey, "rootkey", false, "If true, shows the value of the root key of the certificate chain to be presented to the peer")
+
+	cmdGetDefault.Flags.BoolVar(&flagBlessingsName, "name", false, "If true, shows the value of the blessing name to be presented to the peer")
+	cmdGetDefault.Flags.BoolVar(&flagBlessingsRootKey, "rootkey", false, "If true, shows the value of the root key of the certificate chain to be presented to the peer")
 
 	cmdSet := &cmdline.Command{
 		Name:  "set",
@@ -853,7 +895,7 @@
 
 All blessings are printed to stdout using base64-VOM-encoding.
 `,
-		Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer},
+		Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer, cmdGetTrustedRoots, cmdGetPeerMap},
 	}
 
 	root := &cmdline.Command{
@@ -887,6 +929,24 @@
 	return nil
 }
 
+func printBlessingsInfo(blessings security.Blessings) error {
+	if blessings.IsZero() {
+		return fmt.Errorf("no blessings found")
+	}
+	if flagBlessingsName {
+		fmt.Println(blessings)
+		return nil
+	} else if flagBlessingsRootKey {
+		wire, err := blessings2wire(blessings)
+		if err != nil {
+			return err
+		}
+		fmt.Println(rootkey(wire.CertificateChains[0]))
+		return nil
+	}
+	return dumpBlessings(blessings)
+}
+
 func read(fname string) (string, error) {
 	if len(fname) == 0 {
 		return "", nil
diff --git a/cmd/principal/principal_v23_test.go b/cmd/principal/principal_v23_test.go
index 952bec0..1f3cef1 100644
--- a/cmd/principal/principal_v23_test.go
+++ b/cmd/principal/principal_v23_test.go
@@ -116,12 +116,51 @@
 	got := removePublicKeys(bin.WithEnv(blessEnv).Start("dump").Output())
 	want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
 ---------------- BlessingStore ----------------
-Default blessings: alice
-Peer pattern                   : Blessings
-...                            : alice
+Default Blessings                alice
+Peer pattern                     Blessings
+...                              alice
 ---------------- BlessingRoots ----------------
-Public key                                      : Pattern
-XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
+Public key                                        Pattern
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [alice]
+`
+	if want != got {
+		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
+	}
+}
+
+func V23TestGetRecognizedRoots(t *v23tests.T) {
+	var (
+		outputDir = t.NewTempDir()
+		bin       = t.BuildGoPkg("v.io/x/ref/cmd/principal")
+		aliceDir  = filepath.Join(outputDir, "alice")
+	)
+
+	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
+
+	blessEnv := credEnv(aliceDir)
+	got := removePublicKeys(bin.WithEnv(blessEnv).Start("get", "recognizedroots").Output())
+	want := `Public key                                        Pattern
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [alice]
+`
+	if want != got {
+		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
+	}
+}
+
+func V23TestGetPeermap(t *v23tests.T) {
+	var (
+		outputDir = t.NewTempDir()
+		bin       = t.BuildGoPkg("v.io/x/ref/cmd/principal")
+		aliceDir  = filepath.Join(outputDir, "alice")
+	)
+
+	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
+
+	blessEnv := credEnv(aliceDir)
+	got := removePublicKeys(bin.WithEnv(blessEnv).Start("get", "peermap").Output())
+	want := `Default Blessings                alice
+Peer pattern                     Blessings
+...                              alice
 `
 	if want != got {
 		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
@@ -225,16 +264,16 @@
 	got := removePublicKeys(bin.Start("--v23.credentials="+carolDir, "dump").Output())
 	want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
 ---------------- BlessingStore ----------------
-Default blessings: alice/friend/carol
-Peer pattern                   : Blessings
-...                            : alice/friend/carol
-alice                          : alice/friend/carol/foralice
-bob                            : bob/friend/carol/forbob
+Default Blessings                alice/friend/carol
+Peer pattern                     Blessings
+...                              alice/friend/carol
+alice                            alice/friend/carol/foralice
+bob                              bob/friend/carol/forbob
 ---------------- BlessingRoots ----------------
-Public key                                      : Pattern
-XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
-XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [bob]
-XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [carol]
+Public key                                        Pattern
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [alice]
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [bob]
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [carol]
 `
 	if want != got {
 		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
@@ -263,12 +302,12 @@
 		got := removePublicKeys(bin.Start("--v23.credentials="+alicePhoneDir, "dump").Output())
 		want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
 ---------------- BlessingStore ----------------
-Default blessings: alice/phone
-Peer pattern                   : Blessings
-...                            : alice/phone
+Default Blessings                alice/phone
+Peer pattern                     Blessings
+...                              alice/phone
 ---------------- BlessingRoots ----------------
-Public key                                      : Pattern
-XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
+Public key                                        Pattern
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [alice]
 `
 		if want != got {
 			t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
@@ -298,12 +337,12 @@
 		got := removePublicKeys(bin.Start("--v23.credentials="+alicePhoneCalendarDir, "dump").Output())
 		want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
 ---------------- BlessingStore ----------------
-Default blessings: alice/phone/calendar
-Peer pattern                   : Blessings
-...                            : alice/phone/calendar
+Default Blessings                alice/phone/calendar
+Peer pattern                     Blessings
+...                              alice/phone/calendar
 ---------------- BlessingRoots ----------------
-Public key                                      : Pattern
-XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
+Public key                                        Pattern
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX   [alice]
 `
 		if want != got {
 			t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
@@ -507,24 +546,16 @@
 	// Have bob create a "bob/friend" blessing and have alice recognize that.
 	redirect(t, bin.Start("--v23.credentials="+bobDir, "bless", "--require-caveats=false", aliceDir, "friend"), blessingFile)
 	bin.Start("--v23.credentials="+aliceDir, "addtoroots", blessingFile).WaitOrDie(os.Stdout, os.Stderr)
-	var (
-		// blessing roots lines that should match the keys
-		aliceLine = fmt.Sprintf("%v : [alice]", publicKey(aliceDir))
-		bobLine   = fmt.Sprintf("%v : [bob]", publicKey(bobDir))
 
-		foundAlice, foundBob bool
-	)
-	// Finally dump alice's principal, it should have lines corresponding to aliceLine and bobLine.
-	output := bin.Start("--v23.credentials="+aliceDir, "dump").Output()
-	for _, line := range strings.Split(output, "\n") {
-		if line == aliceLine {
-			foundAlice = true
-		} else if line == bobLine {
-			foundBob = true
-		}
-	}
-	if !foundAlice || !foundBob {
-		t.Fatalf("Got:\n%v\n\nExpected Blessing Roots to include:\n%s\n%s", output, aliceLine, bobLine)
+	want := fmt.Sprintf(`Public key                                        Pattern
+%v   [alice]
+%v   [bob]
+`, publicKey(aliceDir), publicKey(bobDir))
+
+	// Finally view alice's recognized roots, it should have lines corresponding to aliceLine and bobLine.
+	got := bin.Start("--v23.credentials="+aliceDir, "get", "recognizedroots").Output()
+	if got != want {
+		t.Fatalf("Got:\n%v\n\nWant:\n%v", got, want)
 	}
 }
 
@@ -545,7 +576,7 @@
 	bin.Start("--v23.credentials="+aliceDir, "addtoroots", "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9iRjaFDoGJI9tarUwWqIW31ti72krThkYByn1v9Lf89D9VA0Mg2oUL7FDDM7qxjZcVM1ktM_W4tBfMVuRZmVCA==", "some_other_provider").WaitOrDie(os.Stdout, os.Stderr)
 	// "foo" should appear in the set of BlessingRoots
 	output := bin.Start("--v23.credentials="+aliceDir, "dump").Output()
-	want := fmt.Sprintf("41:26:f6:aa:54:f9:31:d4:9d:1f:d2:69:c6:c5:50:70 : [some_other_provider]")
+	want := fmt.Sprintf("41:26:f6:aa:54:f9:31:d4:9d:1f:d2:69:c6:c5:50:70   [some_other_provider]")
 	for _, line := range strings.Split(output, "\n") {
 		if line == want {
 			return
diff --git a/cmd/principal/v23_test.go b/cmd/principal/v23_test.go
index c0d9525..dddc89a 100644
--- a/cmd/principal/v23_test.go
+++ b/cmd/principal/v23_test.go
@@ -32,6 +32,14 @@
 	v23tests.RunTest(t, V23TestDump)
 }
 
+func TestV23GetRecognizedRoots(t *testing.T) {
+	v23tests.RunTest(t, V23TestGetRecognizedRoots)
+}
+
+func TestV23GetPeermap(t *testing.T) {
+	v23tests.RunTest(t, V23TestGetPeermap)
+}
+
 func TestV23RecvBlessings(t *testing.T) {
 	v23tests.RunTest(t, V23TestRecvBlessings)
 }
diff --git a/lib/security/blessingroots.go b/lib/security/blessingroots.go
index 009df29..6ecd0ba 100644
--- a/lib/security/blessingroots.go
+++ b/lib/security/blessingroots.go
@@ -74,12 +74,12 @@
 // DebugString encodes all roots into a string in the following
 // format
 //
-// Public key   : Pattern
-// <public key> : <patterns>
+// Public key     Pattern
+// <public key>   <patterns>
 // ...
-// <public key> : <patterns>
+// <public key>   <patterns>
 func (br *blessingRoots) DebugString() string {
-	const format = "%-47s : %s\n"
+	const format = "%-47s   %s\n"
 	b := bytes.NewBufferString(fmt.Sprintf(format, "Public key", "Pattern"))
 	var s rootSorter
 	for keyBytes, patterns := range br.store {
diff --git a/lib/security/blessingstore.go b/lib/security/blessingstore.go
index bebf3ef..381ec87 100644
--- a/lib/security/blessingstore.go
+++ b/lib/security/blessingstore.go
@@ -150,15 +150,14 @@
 
 // DebugString return a human-readable string encoding of the store
 // in the following format
-// Default blessing : <Default blessing of the store>
-//
-// Peer pattern : Blessings
-// <pattern>    : <blessings>
+// Default Blessings <blessings>
+// Peer pattern   Blessings
+// <pattern>      <blessings>
 // ...
-// <pattern>    : <blessings>
+// <pattern>      <blessings>
 func (bs *blessingStore) DebugString() string {
-	const format = "%-30s : %s\n"
-	b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", bs.state.Default.Blessings()))
+	const format = "%-30s   %s\n"
+	b := bytes.NewBufferString(fmt.Sprintf(format, "Default Blessings", bs.state.Default.Blessings()))
 
 	b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings"))
 
diff --git a/services/device/internal/impl/impl_test.go b/services/device/internal/impl/impl_test.go
index ed8ce4f..fd22cce 100644
--- a/services/device/internal/impl/impl_test.go
+++ b/services/device/internal/impl/impl_test.go
@@ -685,7 +685,8 @@
 	}
 
 	instanceDebug := debug(t, ctx, appID, instance1ID)
-	if !strings.Contains(instanceDebug, fmt.Sprintf("Blessing Store: Default blessings: %s/forapp", test.TestBlessing)) {
+	// Verify the apps default blessings.
+	if !strings.Contains(instanceDebug, fmt.Sprintf("Default Blessings                %s/forapp", test.TestBlessing)) {
 		t.Fatalf("debug response doesn't contain expected info: %v", instanceDebug)
 	}
 
diff --git a/services/device/mgmt_v23_test.go b/services/device/mgmt_v23_test.go
index 7fc2c4f..cead99a 100644
--- a/services/device/mgmt_v23_test.go
+++ b/services/device/mgmt_v23_test.go
@@ -177,7 +177,7 @@
 
 	// Verify the device's default blessing is as expected.
 	inv := debugBin.Start("stats", "read", mtName+"/devmgr/__debug/stats/security/principal/*/blessingstore")
-	inv.ExpectRE(".*Default blessings: root/alice/myworkstation$", -1)
+	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+root/alice/myworkstation$")
 
 	// Get the device's profile, which should be set to non-empty string
 	inv = deviceBin.Start("describe", mtName+"/devmgr/device")
@@ -268,7 +268,7 @@
 
 	// Verify the app's default blessing.
 	inv = debugBin.Start("stats", "read", instanceName+"/stats/security/principal/*/blessingstore")
-	inv.ExpectRE(".*Default blessings: root/alice/myapp$", -1)
+	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+root/alice/myapp$")
 
 	// Stop the instance
 	deviceBin.Run("stop", instanceName)