veyron/tools/principal: Various enhancements.

(1) Introduced the "create" command to initialize a principal with
a customized blessing (as opposed to forcing the "user@host-pid"
string that is automatically generated)

(2) Added a "bless" command to allow one principal to bless another

(3) Invoke AddToRoots in commands that add blessings to the store
(can be disabled with a flag)

(4) Do not require the VEYRON_CREDENTIALS environment variable
for commands like "help" and "dumpblessings", which don't need them.

(5) Update the test script

(6) Separate all store related commands to a sub-command

Change-Id: Ib94539464f90c14d91737717eadbf6d2112cef4c
diff --git a/tools/principal/main.go b/tools/principal/main.go
index a0a61c1..add120f 100644
--- a/tools/principal/main.go
+++ b/tools/principal/main.go
@@ -10,54 +10,65 @@
 	"time"
 
 	"veyron.io/veyron/veyron/lib/cmdline"
+	vsecurity "veyron.io/veyron/veyron/security"
 	"veyron.io/veyron/veyron/services/identity"
 	"veyron.io/veyron/veyron/services/identity/util"
 
+	"veyron.io/veyron/veyron2"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/vdl/vdlutil"
 )
 
+const VEYRON_CREDENTIALS = "VEYRON_CREDENTIALS"
+
 var (
 	// Flags for the "blessself" command
-	flagBlessFor   time.Duration
-	flagAddForPeer string
+	flagBlessSelfFor time.Duration
 
-	// Flags for the "seekblessing" command
-	flagSeekBlessingFrom string
-	flagSkipSetDefault   bool
-	flagForPeer          string
+	// Flags for the "bless" command
+	flagBlessFor  time.Duration
+	flagBlessWith string
+
+	// Flags for the "seekblessings" command
+	flagSeekBlessingsFrom       string
+	flagSeekBlessingsSetDefault bool
+	flagSeekBlessingsForPeer    string
+
+	// Flags common to many commands
+	flagAddToRoots bool
 
 	cmdDump = &cmdline.Command{
 		Name:  "dump",
 		Short: "Dump out information about the principal",
 		Long: `
-Dumps out information about the principal specified by the environment
+Prints out information about the principal specified by the environment
 (VEYRON_CREDENTIALS) that this tool is running in.
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
-			p := rt.R().Principal()
+			p, err := principal()
+			if err != nil {
+				return err
+			}
 			fmt.Printf("Public key : %v\n", p.PublicKey())
-			fmt.Println("")
 			fmt.Println("---------------- BlessingStore ----------------")
 			fmt.Printf("%v", p.BlessingStore().DebugString())
-			fmt.Println("")
 			fmt.Println("---------------- BlessingRoots ----------------")
 			fmt.Printf("%v", p.Roots().DebugString())
 			return nil
 		},
 	}
 
-	cmdPrint = &cmdline.Command{
-		Name:  "print",
-		Short: "Print out information about the provided blessing",
+	cmdDumpBlessings = &cmdline.Command{
+		Name:  "dumpblessings",
+		Short: "Dump out information about the provided blessings",
 		Long: `
-Prints out information about the blessing (typically obtained from this tool)
+Prints out information about the blessings (typically obtained from this tool)
 encoded in the provided file.
 `,
 		ArgsName: "<file>",
 		ArgsLong: `
-<file> is the path to a file containing a blessing typically obtained from
+<file> is the path to a file containing blessings typically obtained from
 this tool. - is used for STDIN.
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
@@ -68,8 +79,21 @@
 			if err != nil {
 				return fmt.Errorf("failed to decode provided blessings: %v", err)
 			}
-			fmt.Printf("Blessings: %v\n", blessings)
-			fmt.Printf("PublicKey: %v\n", blessings.PublicKey())
+			wire := security.MarshalBlessings(blessings)
+			fmt.Printf("Blessings          : %v\n", blessings)
+			fmt.Printf("PublicKey          : %v\n", blessings.PublicKey())
+			fmt.Printf("Certificates       : %d chains with (#certificates, #caveats) = ", len(wire.CertificateChains))
+			for idx, chain := range wire.CertificateChains {
+				ncaveats := 0
+				for _, cert := range chain {
+					ncaveats += len(cert.Caveats)
+				}
+				if idx > 0 {
+					fmt.Printf(" + ")
+				}
+				fmt.Printf("(%d, %d)", len(chain), ncaveats)
+			}
+			fmt.Println("")
 			return nil
 		},
 	}
@@ -101,14 +125,18 @@
 			}
 
 			var caveats []security.Caveat
-			if flagBlessFor != 0 {
-				cav, err := security.ExpiryCaveat(time.Now().Add(flagBlessFor))
+			if flagBlessSelfFor != 0 {
+				cav, err := security.ExpiryCaveat(time.Now().Add(flagBlessSelfFor))
 				if err != nil {
 					return fmt.Errorf("failed to create expiration Caveat: %v", err)
 				}
 				caveats = append(caveats, cav)
 			}
-			blessing, err := rt.R().Principal().BlessSelf(name, caveats...)
+			p, err := principal()
+			if err != nil {
+				return err
+			}
+			blessing, err := p.BlessSelf(name, caveats...)
 			if err != nil {
 				return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
 			}
@@ -117,8 +145,75 @@
 		},
 	}
 
-	cmdForPeer = &cmdline.Command{
-		Name:  "store.forpeer",
+	cmdBless = &cmdline.Command{
+		Name:  "bless",
+		Short: "Bless another principal",
+		Long: `
+	Returns a set of blessings obtained when one principal blesses another.
+
+	The blesser is obtained from the VEYRON_CREDENTIALS environment variable.
+	The principal to be blessed is specified as either a path to the VEYRON_CREDENTIALS directory of the other principal, or the filename (or - for STDIN) of any other blessing of that principal.
+	The blessing that the blesser uses (i.e., which is extended to create the blessing) is the default one from the blessers store, or specified via the --with flag.
+	The blessing is valid only for the duration specified in --for.
+
+	For example, let's say a principal with the default blessing "alice" wants to bless another principal as "alice/bob", the invocation would be:
+	VEYRON_CREDENTIALS=<path to alice> principal bless <path to bob> friend
+	`,
+		ArgsName: "<principal to bless> <extension>",
+		ArgsLong: `
+	<principal to bless> represents the principal to be blessed (i.e., whose public key will be provided with a name).
+	This can either be a path to a file containing any other set of blessings of that principal (or - for STDIN) or the
+	path to the VEYRON_CREDENTIALS directory of that principal.
+
+	<extension> is the string extension that will be applied to create the blessing.
+	`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			if len(args) != 2 {
+				return fmt.Errorf("require exactly two arguments, provided %d", len(args))
+			}
+			p, err := principal()
+			if err != nil {
+				return err
+			}
+
+			var with security.Blessings
+			if len(flagBlessWith) > 0 {
+				if with, err = decodeBlessings(flagBlessWith); err != nil {
+					return fmt.Errorf("failed to read blessings from --with=%q: %v", flagBlessWith, err)
+				}
+			} else {
+				with = p.BlessingStore().Default()
+			}
+
+			var key security.PublicKey
+			tobless, extension := args[0], args[1]
+			if finfo, err := os.Stat(tobless); err == nil && finfo.IsDir() {
+				other, _, err := vsecurity.NewPersistentPrincipal(tobless)
+				if err != nil {
+					return fmt.Errorf("failed to read principal in directory %q: %v", tobless, err)
+				}
+				key = other.PublicKey()
+			} else if other, err := decodeBlessings(tobless); err != nil {
+				return fmt.Errorf("failed to decode blessings in %q: %v", tobless, err)
+			} else {
+				key = other.PublicKey()
+			}
+
+			caveat, err := security.ExpiryCaveat(time.Now().Add(flagBlessFor))
+			if err != nil {
+				return fmt.Errorf("failed to create ExpirtyCaveat: %v", err)
+			}
+
+			blessings, err := p.Bless(key, with, extension, caveat)
+			if err != nil {
+				return fmt.Errorf("Bless(%v, %v, %q, ExpiryCaveat(%v)) failed: %v", key, with, extension, flagBlessFor, err)
+			}
+			return dumpBlessings(blessings)
+		},
+	}
+
+	cmdStoreForPeer = &cmdline.Command{
+		Name:  "forpeer",
 		Short: "Return blessings marked for the provided peer",
 		Long: `
 Returns blessings that are marked for the provided peer in the
@@ -134,12 +229,16 @@
 blessings set on the store with the "..." pattern).
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
-			return dumpBlessings(rt.R().Principal().BlessingStore().ForPeer(args...))
+			p, err := principal()
+			if err != nil {
+				return err
+			}
+			return dumpBlessings(p.BlessingStore().ForPeer(args...))
 		},
 	}
 
-	cmdDefault = &cmdline.Command{
-		Name:  "store.default",
+	cmdStoreDefault = &cmdline.Command{
+		Name:  "default",
 		Short: "Return blessings marked as default",
 		Long: `
 Returns blessings that are marked as default in the BlessingStore
@@ -147,12 +246,16 @@
 is running in.
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
-			return dumpBlessings(rt.R().Principal().BlessingStore().Default())
+			p, err := principal()
+			if err != nil {
+				return err
+			}
+			return dumpBlessings(p.BlessingStore().Default())
 		},
 	}
 
-	cmdSet = &cmdline.Command{
-		Name:  "store.set",
+	cmdStoreSet = &cmdline.Command{
+		Name:  "set",
 		Short: "Set provided blessings for peer",
 		Long: `
 Marks the provided blessings to be shared with the provided
@@ -179,22 +282,31 @@
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
 			if len(args) != 2 {
-				return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", cmd.Name, len(args))
+				return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", len(args))
 			}
 			blessings, err := decodeBlessings(args[0])
 			if err != nil {
 				return fmt.Errorf("failed to decode provided blessings: %v", err)
 			}
 			pattern := security.BlessingPattern(args[1])
-			if _, err := rt.R().Principal().BlessingStore().Set(blessings, pattern); err != nil {
+			p, err := principal()
+			if err != nil {
+				return err
+			}
+			if _, err := p.BlessingStore().Set(blessings, pattern); err != nil {
 				return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
 			}
+			if flagAddToRoots {
+				if err := p.AddToRoots(blessings); err != nil {
+					return fmt.Errorf("AddToRoots failed: %v", err)
+				}
+			}
 			return nil
 		},
 	}
 
-	cmdSetDefault = &cmdline.Command{
-		Name:  "store.setdefault",
+	cmdStoreSetDefault = &cmdline.Command{
+		Name:  "setdefault",
 		Short: "Set provided blessings as default",
 		Long: `
 Sets the provided blessings as default in the BlessingStore specified
@@ -210,15 +322,64 @@
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
 			if len(args) != 1 {
-				return fmt.Errorf("requires exactly one argument, <file>, provided %d", cmd.Name, len(args))
+				return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
 			}
 			blessings, err := decodeBlessings(args[0])
 			if err != nil {
 				return fmt.Errorf("failed to decode provided blessings: %v", err)
 			}
-			if err := rt.R().Principal().BlessingStore().SetDefault(blessings); err != nil {
+			p, err := principal()
+			if err != nil {
+				return err
+			}
+			if err = p.BlessingStore().SetDefault(blessings); err != nil {
 				return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
 			}
+			if flagAddToRoots {
+				if err := p.AddToRoots(blessings); err != nil {
+					return fmt.Errorf("AddToRoots failed: %v", err)
+				}
+			}
+			return nil
+		},
+	}
+
+	cmdCreate = &cmdline.Command{
+		Name:  "create",
+		Short: "Create a new principal and persist it into a directory",
+		Long: `
+	Creates a new principal with a single self-blessed blessing and writes it out
+	to the provided directory. The same directory can be used to set the VEYRON_CREDENTIALS
+	environment variables for other veyron applications.
+	`,
+		ArgsName: "<directory> <blessing>",
+		ArgsLong: `
+	<directory> is the directory to which the principal will be persisted.
+	<blessing> is the self-blessed blessing that the principal will be setup to use by default.
+	`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			if len(args) != 2 {
+				return fmt.Errorf("requires exactly two arguments: <directory> and <blessing>, provided %d", len(args))
+			}
+			dir, name := args[0], args[1]
+			p, existed, err := vsecurity.NewPersistentPrincipal(dir)
+			if existed {
+				return fmt.Errorf("principal already exists in %q", dir)
+			}
+			blessings, err := p.BlessSelf(name)
+			if err != nil {
+				return fmt.Errorf("BlessSelf(%q) failed: %v", name, err)
+			}
+			if err := p.BlessingStore().SetDefault(blessings); err != nil {
+				return fmt.Errorf("BlessingStore.SetDefault(%v) failed: %v", blessings, err)
+			}
+			if _, err := p.BlessingStore().Set(blessings, security.AllPrincipals); err != nil {
+				return fmt.Errorf("BlessingStore.Set(%v, %q) failed: %v", blessings, security.AllPrincipals, err)
+			}
+			if err := p.AddToRoots(blessings); err != nil {
+				return fmt.Errorf("AddToRoots(%v) failed: %v", blessings, err)
+			}
+			fmt.Printf("%s=%q\n", VEYRON_CREDENTIALS, dir)
 			return nil
 		},
 	}
@@ -241,14 +402,18 @@
 		Run: func(cmd *cmdline.Command, args []string) error {
 			blessedChan := make(chan string)
 			defer close(blessedChan)
-			macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingFrom, blessedChan)
+			macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingsFrom, blessedChan)
 			if err != nil {
 				return fmt.Errorf("failed to get macaroon from Veyron blesser: %v", err)
 			}
 			macaroon := <-macaroonChan
 			service := <-macaroonChan
 
-			ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+			r, err := runtime()
+			if err != nil {
+				return err
+			}
+			ctx, cancel := r.NewContext().WithTimeout(time.Minute)
 			defer cancel()
 
 			var reply vdlutil.Any
@@ -271,14 +436,20 @@
 			// Wait for getTokenForBlessRPC to clean up:
 			<-macaroonChan
 
-			if !flagSkipSetDefault {
-				if err := rt.R().Principal().BlessingStore().SetDefault(blessings); err != nil {
+			if flagSeekBlessingsSetDefault {
+				if err := r.Principal().BlessingStore().SetDefault(blessings); err != nil {
 					return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
 				}
 			}
-			pattern := security.BlessingPattern(flagForPeer)
-			if _, err := rt.R().Principal().BlessingStore().Set(blessings, pattern); err != nil {
-				return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
+			if pattern := security.BlessingPattern(flagSeekBlessingsForPeer); len(pattern) > 0 {
+				if _, err := r.Principal().BlessingStore().Set(blessings, pattern); err != nil {
+					return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
+				}
+			}
+			if flagAddToRoots {
+				if err := r.Principal().AddToRoots(blessings); err != nil {
+					return fmt.Errorf("AddToRoots failed: %v", err)
+				}
 			}
 			return dumpBlessings(blessings)
 		},
@@ -286,16 +457,26 @@
 )
 
 func main() {
-	if len(os.Getenv("VEYRON_CREDENTIALS")) == 0 {
-		// TODO(ataly, ashankar): Handle this case
-		fmt.Fprintf(os.Stderr, "ERROR: Please set the VEYRON_CREDENTIALS environment variable\n")
-		os.Exit(2)
+	cmdBlessSelf.Flags.DurationVar(&flagBlessSelfFor, "for", 0, "Duration of blessing validity (zero means no that the blessing is always valid)")
+	cmdBless.Flags.DurationVar(&flagBlessFor, "for", time.Minute, "Duration of blessing validity")
+	cmdBless.Flags.StringVar(&flagBlessWith, "with", "", "Path to file containing blessing to extend. ")
+	cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsFrom, "from", "https://proxy.envyor.com:8125/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")
+	cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsForPeer, "for_peer", string(security.AllPrincipals), "If non-empty, the blessings obtained will be marked for peers matching this pattern in the store")
+	cmdSeekBlessings.Flags.BoolVar(&flagAddToRoots, "add_to_roots", true, "If true, the root certificate of the blessing will be added to the principal's set of recognized root certificates")
+	cmdStoreSet.Flags.BoolVar(&flagAddToRoots, "add_to_roots", true, "If true, the root certificate of the blessing will be added to the principal's set of recognized root certificates")
+	cmdStoreSetDefault.Flags.BoolVar(&flagAddToRoots, "add_to_roots", true, "If true, the root certificate of the blessing will be added to the principal's set of recognized root certificates")
+
+	cmdStore := &cmdline.Command{
+		Name:  "store",
+		Short: "Manipulate and inspect the principal's blessing store",
+		Long: `
+Commands to manipulate and inspect the blessing store of the principal.
+
+All blessings are printed to stdout using base64-VOM-encoding
+`,
+		Children: []*cmdline.Command{cmdStoreDefault, cmdStoreSetDefault, cmdStoreForPeer, cmdStoreSet},
 	}
-	rt.Init()
-	cmdBlessSelf.Flags.DurationVar(&flagBlessFor, "for", 0*time.Hour, "Expiry time of Blessing (optional)")
-	cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingFrom, "from", "https://proxy.envyor.com:8125/google", "URL to use to begin the seek blessings process")
-	cmdSeekBlessings.Flags.BoolVar(&flagSkipSetDefault, "skip_set_default", false, "flag to indicate that the blessings obtained from the Veyron blesser must not be set as default on the principals's blessing store")
-	cmdSeekBlessings.Flags.StringVar(&flagForPeer, "for_peer", "...", "pattern to be used while setting the blessings obtained from the Veyron blesser on the principal's blessing store")
 
 	(&cmdline.Command{
 		Name:  "principal",
@@ -306,10 +487,25 @@
 
 All objects are printed using base64-VOM-encoding.
 `,
-		Children: []*cmdline.Command{cmdDump, cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer, cmdSetDefault, cmdSet, cmdSeekBlessings},
+		Children: []*cmdline.Command{cmdCreate, cmdSeekBlessings, cmdDump, cmdDumpBlessings, cmdBlessSelf, cmdBless, cmdStore},
 	}).Main()
 }
 
+func runtime() (veyron2.Runtime, error) {
+	if len(os.Getenv(VEYRON_CREDENTIALS)) == 0 {
+		return nil, fmt.Errorf("VEYRON_CREDENTIALS environment variable must be set")
+	}
+	return rt.Init(), nil
+}
+
+func principal() (security.Principal, error) {
+	r, err := runtime()
+	if err != nil {
+		return nil, err
+	}
+	return r.Principal(), nil
+}
+
 func decodeBlessings(fname string) (security.Blessings, error) {
 	var wire security.WireBlessings
 	if err := decode(fname, &wire); err != nil {
diff --git a/tools/principal/test.sh b/tools/principal/test.sh
index 86e5fd2..d0a18d0 100755
--- a/tools/principal/test.sh
+++ b/tools/principal/test.sh
@@ -14,85 +14,85 @@
   veyron go build veyron.io/veyron/veyron/tools/principal || shell_test::fail "line ${LINENO}: failed to build principal"
 }
 
-extractBlessings() {
-    awk '/Blessings/ { st = index($0," "); print substr($0,st+1)}'
+# rmpublickey replaces public keys (16 hex bytes, :-separated) with XX:....
+# This substitution enables comparison with golden output even when keys are freshly
+# minted by the "principal create" command.
+rmpublickey() {
+    sed -e "s/\([0-9a-f]\{2\}:\)\{15\}[0-9a-f]\{2\}/XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX/g"
+}
+
+dumpblessings() {
+    ./principal dumpblessings "$1" | rmpublickey
 }
 
 main() {
-  local GOT WANT
-
-  # Build binaries.
   cd "${WORKDIR}"
   build
 
-  # Set VEYRON_CREDENTIALS.
-  export VEYRON_CREDENTIALS="${WORKDIR}"
+  # Prevent any VEYRON_CREDENTIALS in the environment from interfering with this test.
+  unset VEYRON_CREDENTIALS
+  # Create two principals, one called "alice" one called "bob"
+  ./principal create ./alice alice >/dev/null || shell_test::fail "line ${LINENO}: create failed"
+  ./principal create ./bob bob >/dev/null || shell_test::fail "line ${LINENO}: create failed"
+  # Run dump, bless, blessself on alice
+  export VEYRON_CREDENTIALS=./alice
+  ./principal blessself alicereborn >alice.blessself || shell_test::fail "line ${LINENO}: blessself failed"
+  ./principal bless ./bob friend >alice.bless || shell_test::fail "line ${LINENO}: bless failed"
+  ./principal dump >alice.dump || shell_test::fail "line ${LINENO}: dump failed"
+  # Run store setdefault, store default, store set, store forpeer on bob
+  export VEYRON_CREDENTIALS=./bob
+  ./principal store setdefault alice.bless || shell_test::fail "line ${LINENO}: store setdefault failed"
+  ./principal store default >bob.store.default || shell_test::fail "line ${LINENO}: store default failed"
+  ./principal store set alice.bless alice/... || shell_test::fail "line ${LINENO}: store set failed"
+  ./principal store forpeer alice/server >bob.store.forpeer || shell_test::fail "line ${LINENO}: store forpeer failed" 
+  # Any other commands to be run without VEYRON_CREDENTIALS set.
+  unset VEYRON_CREDENTIALS
 
-  ./principal dump >/dev/null || shell_test::fail "line ${LINENO}: dump failed"
-  ./principal blessself >/dev/null || shell_test::fail "line ${LINENO}: blessself failed"
-  ./principal blessself alice >alice || shell_test::fail "line ${LINENO}: blessself alice failed"
-  ./principal store.default >/dev/null || shell_test::fail "line ${LINENO}: store.default failed"
-  ./principal store.forpeer >/dev/null || shell_test::fail "line ${LINENO}: store.forpeer failed"
+  # Validate the output of various commands (mostly using "principal dumpblessings")
+  cat alice.dump | rmpublickey >got || shell_test::fail "line ${LINENO}: cat alice.dump | rmpublickey failed"
+  cat >want <<EOF
+Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
+---------------- BlessingStore ----------------
+Default blessings: alice(0 caveats)
+Peer pattern                   : Blessings
+...                            : alice(0 caveats)
+---------------- BlessingRoots ----------------
+Public key                                      : Pattern
+XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice/...]
+EOF
+  if ! diff got want; then
+  	shell_test::fail "line ${LINENO}"
+  fi
 
-  # Test print
-  GOT=$(./principal print alice | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="alice(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
+  dumpblessings alice.blessself >got || shell_test::fail "line ${LINENO}: dumpblessings failed"
+  cat >want <<EOF
+Blessings          : alicereborn(0 caveats)
+PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
+Certificates       : 1 chains with (#certificates, #caveats) = (1, 0)
+EOF
+  if ! diff got want; then
+  	shell_test::fail "line ${LINENO}"
+  fi
 
-  GOT=$(./principal blessself bob | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="bob(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
+  dumpblessings bob.store.default >got || shell_test::fail "line ${LINENO}: dumpblessings failed"
+  cat >want <<EOF
+Blessings          : alice/friend(1 caveats)
+PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
+Certificates       : 1 chains with (#certificates, #caveats) = (2, 1)
+EOF
+  if ! diff got want; then
+	shell_test::fail "line ${LINENO}"
+  fi
 
-  GOT=$(./principal blessself --for=1h bob| ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="bob(1 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  # Test store.default, store.setdefault
-  ./principal blessself testFile >f || shell_test::fail "line ${LINENO}: blessself testFile failed"
-  ./principal store.setdefault f || shell_test::fail "line ${LINENO}: store.setdefault failed"
-  GOT=$(./principal store.default | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="testFile(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  ./principal blessself testStdin | ./principal store.setdefault - || shell_test::fail "line ${LINENO}: blessself testStdin | store.setdefault - failed"
-  GOT=$(./principal store.default | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="testStdin(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  # Test store.forpeer, store.set
-  ./principal blessself forAlice >f || shell_test::fail "line ${LINENO}: blessself forAlice failed"
-  ./principal store.set f alice/... || shell_test::fail "line ${LINENO}: store.set failed"
-  ./principal blessself forAll | ./principal store.set - ... || shell_test::fail "line ${LINENO}: blessself forAll | store.set - ... failed"
-
-  GOT=$(./principal store.forpeer | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="forAll(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  GOT=$(./principal store.forpeer bob | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="forAll(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  GOT=$(./principal store.forpeer alice | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="forAlice(0 caveats)#forAll(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  GOT=$(./principal store.forpeer alice/friend | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="forAlice(0 caveats)#forAll(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
-  GOT=$(./principal store.forpeer alice/friend bob/spouse | ./principal print - | extractBlessings) \
-    || shell_test::fail "line ${LINENO}: failed to run principal"
-  WANT="forAlice(0 caveats)#forAll(0 caveats)"
-  shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
+  dumpblessings bob.store.forpeer >got || shell_test::fail "line ${LINENO}: dumpblessings failed"
+  cat >want <<EOF
+Blessings          : bob(0 caveats)#alice/friend(1 caveats)
+PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
+Certificates       : 2 chains with (#certificates, #caveats) = (1, 0) + (2, 1)
+EOF
+  if ! diff got want; then
+	shell_test::fail "line ${LINENO}"
+  fi
 
   shell_test::pass
 }