From b5e306b12ae2ad040152cd64240314c3ad93dc93 Mon Sep 17 00:00:00 2001 From: Thomas Fossati Date: Fri, 1 Oct 2021 21:27:53 +0100 Subject: [PATCH] CLI, "comid display" subcommand --- cli/cmd/comidDisplay.go | 111 ++++++++++++++++++++++++++++++++ cli/cmd/comidDisplay_test.go | 119 +++++++++++++++++++++++++++++++++++ comid/comid.go | 13 +++- comid/example_test.go | 9 ++- comid/measurement.go | 4 ++ 5 files changed, 252 insertions(+), 4 deletions(-) create mode 100644 cli/cmd/comidDisplay.go create mode 100644 cli/cmd/comidDisplay_test.go diff --git a/cli/cmd/comidDisplay.go b/cli/cmd/comidDisplay.go new file mode 100644 index 00000000..a7d04c5c --- /dev/null +++ b/cli/cmd/comidDisplay.go @@ -0,0 +1,111 @@ +// Copyright 2021 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "errors" + "fmt" + + "github.com/spf13/afero" + "github.com/spf13/cobra" + "github.com/veraison/corim/comid" +) + +var ( + comidDisplayFiles []string + comidDisplayDirs []string +) + +var comidDisplayCmd = NewComidDisplayCmd() + +func NewComidDisplayCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "display", + Short: "display one or more CBOR-encoded CoMID(s) in human readable (JSON) format", + Long: `display one or more CBOR-encoded CoMID(s) in human readable (JSON) format", + + Display CoMIDs in file c.cbor. + + cli comid display --file=c.cbor + + Display CoMIDs in files c1.cbor, c2.cbor and any cbor file in the comids/ + directory. + + cli comid display --file=c1.cbor --file=c2.cbor --dir=comids + `, + + RunE: func(cmd *cobra.Command, args []string) error { + if err := checkComidDisplayArgs(); err != nil { + return err + } + + filesList := filesList(comidDisplayFiles, comidDisplayDirs, ".cbor") + if len(filesList) == 0 { + return errors.New("no files found") + } + + errs := 0 + for _, file := range filesList { + if err := displayComid(file); err != nil { + fmt.Printf("failed displaying %q: %v", file, err) + errs++ + continue + } + } + + if errs != 0 { + return fmt.Errorf("%d/%d display(s) failed", errs, len(filesList)) + } + return nil + }, + } + + cmd.Flags().StringArrayVarP( + &comidDisplayFiles, "file", "f", []string{}, "a CoMID file (in CBOR format)", + ) + + cmd.Flags().StringArrayVarP( + &comidDisplayDirs, "dir", "d", []string{}, "a directory containing CoMID files (in CBOR format)", + ) + + return cmd +} + +func displayComid(file string) error { + var ( + data []byte + err error + c comid.Comid + ) + + if data, err = afero.ReadFile(fs, file); err != nil { + return fmt.Errorf("error loading CoMID from %s: %w", file, err) + } + + if err = c.FromCBOR(data); err != nil { + return fmt.Errorf("error decoding CoMID from %s: %w", file, err) + } + + prettyPrint := true + json, err := c.ToJSON(prettyPrint) + if err != nil { + return fmt.Errorf("error JSON encoding CoMID %s: %w", file, err) + } + + fmt.Println("[ ", file, " ]") + fmt.Println(string(json)) + + return nil +} + +func checkComidDisplayArgs() error { + if len(comidDisplayFiles) == 0 && len(comidDisplayDirs) == 0 { + return errors.New("no files supplied") + } + return nil +} + +func init() { + comidCmd.AddCommand(comidDisplayCmd) +} diff --git a/cli/cmd/comidDisplay_test.go b/cli/cmd/comidDisplay_test.go new file mode 100644 index 00000000..198f41be --- /dev/null +++ b/cli/cmd/comidDisplay_test.go @@ -0,0 +1,119 @@ +// Copyright 2021 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_ComidDisplayCmd_unknown_argument(t *testing.T) { + cmd := NewComidDisplayCmd() + + args := []string{"--unknown-argument=val"} + cmd.SetArgs(args) + + err := cmd.Execute() + assert.EqualError(t, err, "unknown flag: --unknown-argument") +} + +func Test_ComidDisplayCmd_no_files(t *testing.T) { + cmd := NewComidDisplayCmd() + + // no args + + err := cmd.Execute() + assert.EqualError(t, err, "no files supplied") +} + +func Test_ComidDisplayCmd_no_files_found(t *testing.T) { + cmd := NewComidDisplayCmd() + + args := []string{ + "--file=unknown", + "--dir=unsure", + } + cmd.SetArgs(args) + + err := cmd.Execute() + assert.EqualError(t, err, "no files found") +} + +func Test_ComidDisplayCmd_file_with_invalid_cbor(t *testing.T) { + var err error + + cmd := NewComidDisplayCmd() + + fs = afero.NewMemMapFs() + err = afero.WriteFile(fs, "invalid.cbor", []byte{0xff, 0xff}, 0400) + require.NoError(t, err) + + args := []string{ + "--file=invalid.cbor", + } + cmd.SetArgs(args) + + err = cmd.Execute() + assert.EqualError(t, err, "1/1 display(s) failed") +} + +func Test_ComidDisplayCmd_file_with_invalid_comid(t *testing.T) { + var err error + + cmd := NewComidDisplayCmd() + + fs = afero.NewMemMapFs() + err = afero.WriteFile(fs, "bad-comid.cbor", []byte{0xa0}, 0400) + require.NoError(t, err) + + args := []string{ + "--file=bad-comid.cbor", + } + cmd.SetArgs(args) + + err = cmd.Execute() + assert.EqualError(t, err, "1/1 display(s) failed") +} + +func Test_ComidDisplayCmd_file_with_valid_comid(t *testing.T) { + var err error + + cmd := NewComidDisplayCmd() + + fs = afero.NewMemMapFs() + err = afero.WriteFile(fs, "ok.cbor", PSARefValCBOR, 0400) + require.NoError(t, err) + + args := []string{ + "--file=ok.cbor", + } + cmd.SetArgs(args) + + fmt.Printf("%x\n", PSARefValCBOR) + + err = cmd.Execute() + assert.NoError(t, err) +} + +func Test_ComidDisplayCmd_file_with_valid_comid_from_dir(t *testing.T) { + var err error + + cmd := NewComidDisplayCmd() + + fs = afero.NewMemMapFs() + err = afero.WriteFile(fs, "testdir/ok.cbor", PSARefValCBOR, 0400) + require.NoError(t, err) + + args := []string{ + "--dir=testdir", + } + cmd.SetArgs(args) + + err = cmd.Execute() + assert.NoError(t, err) +} diff --git a/comid/comid.go b/comid/comid.go index 5986a4a8..a7e4fbac 100644 --- a/comid/comid.go +++ b/comid/comid.go @@ -234,6 +234,10 @@ func (o Comid) Valid() error { // ToCBOR serializes the target Comid to CBOR func (o Comid) ToCBOR() ([]byte, error) { + if err := o.Valid(); err != nil { + return nil, err + } + return em.Marshal(&o) } @@ -248,6 +252,13 @@ func (o *Comid) FromJSON(data []byte) error { } // ToJSON serializes the target Comid to JSON -func (o Comid) ToJSON() ([]byte, error) { +func (o Comid) ToJSON(prettyPrint bool) ([]byte, error) { + if err := o.Valid(); err != nil { + return nil, err + } + + if prettyPrint { + return json.MarshalIndent(&o, "", " ") + } return json.Marshal(&o) } diff --git a/comid/example_test.go b/comid/example_test.go index 1fdcb5e8..674d902d 100644 --- a/comid/example_test.go +++ b/comid/example_test.go @@ -107,7 +107,8 @@ func Example_encode() { fmt.Printf("%x\n", cbor) } - json, err := comid.ToJSON() + prettyPrint := false + json, err := comid.ToJSON(prettyPrint) if err == nil { fmt.Printf("%s\n", string(json)) } @@ -163,7 +164,8 @@ func Example_encode_PSA() { fmt.Printf("%x\n", cbor) } - json, err := comid.ToJSON() + prettyPrint := false + json, err := comid.ToJSON(prettyPrint) if err == nil { fmt.Printf("%s\n", string(json)) } @@ -195,7 +197,8 @@ func Example_encode_PSA_attestation_verification() { fmt.Printf("%x\n", cbor) } - json, err := comid.ToJSON() + prettyPrint := false + json, err := comid.ToJSON(prettyPrint) if err == nil { fmt.Printf("%s", string(json)) } diff --git a/comid/measurement.go b/comid/measurement.go index a97ffa47..ec1be7a2 100644 --- a/comid/measurement.go +++ b/comid/measurement.go @@ -125,6 +125,10 @@ func (o Mkey) MarshalCBOR() ([]byte, error) { return em.Marshal(o.val) } +func (o *Mkey) UnmarshalCBOR(data []byte) error { + return dm.Unmarshal(data, &o.val) +} + // Mval stores a measurement-values-map with JSON and CBOR serializations. type Mval struct { Ver *Version `cbor:"0,keyasint,omitempty" json:"version,omitempty"`